Skip to content

Commit 0293d79

Browse files
committed
generalize Completer for .cabal and cabal.project files
1 parent 0a6ed29 commit 0293d79

File tree

12 files changed

+122
-102
lines changed

12 files changed

+122
-102
lines changed

haskell-language-server.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,7 @@ library hls-cabal-project-plugin
342342
Ide.Plugin.CabalProject.Types
343343
Ide.Plugin.CabalProject.Completion.Completions
344344
Ide.Plugin.CabalProject.Completion.Data
345+
Ide.Plugin.CabalProject.Completion.Completer.Types
345346

346347
build-depends:
347348
, bytestring

plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/Completion/Completer/FilePath.hs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ import qualified Text.Fuzzy.Parallel as Fuzzy
2121

2222
-- | Completer to be used when a file path can be completed for a field.
2323
-- Completes file paths as well as directories.
24-
filePathCompleter :: Completer
24+
filePathCompleter :: HasPrefixInfo d => Completer d
2525
filePathCompleter recorder cData = do
26-
let prefInfo = cabalPrefixInfo cData
26+
let prefInfo = getPrefixInfo cData
2727
complInfo = pathCompletionInfoFromCabalPrefixInfo "" prefInfo
2828
filePathCompletions <- listFileCompletions recorder complInfo
2929
let scored =
@@ -40,7 +40,7 @@ filePathCompleter recorder cData = do
4040
pure $ mkCompletionItem (completionRange prefInfo) fullFilePath fullFilePath
4141
)
4242

43-
mainIsCompleter :: (Maybe StanzaName -> GenericPackageDescription -> [FilePath]) -> Completer
43+
mainIsCompleter :: (Maybe StanzaName -> GenericPackageDescription -> [FilePath]) -> CabalCompleter
4444
mainIsCompleter extractionFunction recorder cData = do
4545
mGPD <- getLatestGPD cData
4646
case mGPD of
@@ -74,9 +74,9 @@ mainIsCompleter extractionFunction recorder cData = do
7474

7575
-- | Completer to be used when a directory can be completed for the field.
7676
-- Only completes directories.
77-
directoryCompleter :: Completer
77+
directoryCompleter :: HasPrefixInfo d => Completer d
7878
directoryCompleter recorder cData = do
79-
let prefInfo = cabalPrefixInfo cData
79+
let prefInfo = getPrefixInfo cData
8080
complInfo = pathCompletionInfoFromCabalPrefixInfo "" prefInfo
8181
directoryCompletions <- listDirectoryCompletions recorder complInfo
8282
let scored =

plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/Completion/Completer/Module.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import qualified Text.Fuzzy.Parallel as Fuzzy
2727
--
2828
-- Takes an extraction function which extracts the source directories
2929
-- to be used by the completer.
30-
modulesCompleter :: (Maybe StanzaName -> GenericPackageDescription -> [FilePath]) -> Completer
30+
modulesCompleter :: (Maybe StanzaName -> GenericPackageDescription -> [FilePath]) -> CabalCompleter
3131
modulesCompleter extractionFunction recorder cData = do
3232
mGPD <- getLatestGPD cData
3333
case mGPD of

plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/Completion/Completer/Simple.hs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,21 +26,21 @@ import qualified Text.Fuzzy.Parallel as Fuzzy
2626

2727
-- | Completer to be used when no completion suggestions
2828
-- are implemented for the field
29-
noopCompleter :: Completer
29+
noopCompleter :: Completer d
3030
noopCompleter _ _ = pure []
3131

3232
-- | Completer to be used when no completion suggestions
3333
-- are implemented for the field and a log message should be emitted.
34-
errorNoopCompleter :: Log -> Completer
34+
errorNoopCompleter :: Log -> Completer d
3535
errorNoopCompleter l recorder _ = do
3636
logWith recorder Warning l
3737
pure []
3838

3939
-- | Completer to be used when a simple set of values
4040
-- can be completed for a field.
41-
constantCompleter :: [T.Text] -> Completer
41+
constantCompleter :: [T.Text] -> HasPrefixInfo d => Completer d
4242
constantCompleter completions _ cData = do
43-
let prefInfo = cabalPrefixInfo cData
43+
let prefInfo = getPrefixInfo cData
4444
scored = Fuzzy.simpleFilter Fuzzy.defChunkSize Fuzzy.defMaxResults (completionPrefix prefInfo) completions
4545
range = completionRange prefInfo
4646
pure $ map (mkSimpleCompletionItem range . Fuzzy.original) scored
@@ -49,7 +49,7 @@ constantCompleter completions _ cData = do
4949
--
5050
-- TODO: Does not exclude imports, defined after the current cursor position
5151
-- which are not allowed according to the cabal specification
52-
importCompleter :: Completer
52+
importCompleter :: CabalCompleter
5353
importCompleter l cData = do
5454
cabalCommonsM <- getCabalCommonSections cData
5555
case cabalCommonsM of
@@ -66,7 +66,7 @@ importCompleter l cData = do
6666
-- This is almost always the name of the cabal file. However,
6767
-- it is not forbidden by the specification to have a different name,
6868
-- it is just forbidden on hackage.
69-
nameCompleter :: Completer
69+
nameCompleter :: CabalCompleter
7070
nameCompleter _ cData = do
7171
let scored = Fuzzy.simpleFilter Fuzzy.defChunkSize Fuzzy.defMaxResults (completionPrefix prefInfo) [completionFileName prefInfo]
7272
prefInfo = cabalPrefixInfo cData
@@ -80,7 +80,7 @@ nameCompleter _ cData = do
8080
-- the value in the completion suggestion.
8181
--
8282
-- If the value does not occur in the weighted map its weight is defaulted to zero.
83-
weightedConstantCompleter :: [T.Text] -> Map T.Text Double -> Completer
83+
weightedConstantCompleter :: [T.Text] -> Map T.Text Double -> CabalCompleter
8484
weightedConstantCompleter completions weights _ cData = do
8585
let scored =
8686
if perfectScore > 0

plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/Completion/Completer/Snippet.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import qualified Language.LSP.Protocol.Types as LSP
1818
import qualified Text.Fuzzy.Parallel as Fuzzy
1919

2020
-- | Maps snippet triggerwords with their completers
21-
snippetCompleter :: Completer
21+
snippetCompleter :: CabalCompleter
2222
snippetCompleter recorder cData = do
2323
let scored = Fuzzy.simpleFilter Fuzzy.defChunkSize Fuzzy.defMaxResults (completionPrefix prefInfo) $ Map.keys snippets
2424
mapMaybeM

plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/Completion/Completer/Types.hs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,12 @@ import qualified Distribution.Parsec.Position as Syntax
99
import Ide.Plugin.Cabal.Completion.Types
1010
import Language.LSP.Protocol.Types (CompletionItem)
1111

12-
-- | Takes information needed to build possible completion items
12+
-- | Takes information and completer type needed to build possible completion items
1313
-- and returns the list of possible completion items
14-
type Completer = Recorder (WithPriority Log) -> CompleterData -> IO [CompletionItem]
14+
type Completer d = Recorder (WithPriority Log) -> d -> IO [CompletionItem]
15+
16+
-- Cabal specific completer
17+
type CabalCompleter = Completer CompleterData
1518

1619
-- | Contains information to be used by completers.
1720
data CompleterData = CompleterData
@@ -26,3 +29,10 @@ data CompleterData = CompleterData
2629
-- | The name of the stanza in which the completer is applied
2730
stanzaName :: Maybe StanzaName
2831
}
32+
33+
-- Allows CabalCompleter and CabalProjectCompleter to be passed into the same completers
34+
class HasPrefixInfo d where
35+
getPrefixInfo :: d -> CabalPrefixInfo
36+
37+
instance HasPrefixInfo CompleterData where
38+
getPrefixInfo = cabalPrefixInfo

plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/Completion/Completions.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import qualified Distribution.Parsec.Position as Syntax
1515
import Ide.Plugin.Cabal.Completion.CabalFields
1616
import Ide.Plugin.Cabal.Completion.Completer.Simple
1717
import Ide.Plugin.Cabal.Completion.Completer.Snippet
18-
import Ide.Plugin.Cabal.Completion.Completer.Types (Completer)
18+
import Ide.Plugin.Cabal.Completion.Completer.Types (CabalCompleter)
1919
import Ide.Plugin.Cabal.Completion.Data
2020
import Ide.Plugin.Cabal.Completion.Types
2121
import qualified Language.LSP.Protocol.Lens as JL
@@ -28,7 +28,7 @@ import System.FilePath (takeBaseName)
2828

2929
-- | Takes information about the completion context within the file
3030
-- and finds the correct completer to be applied.
31-
contextToCompleter :: Context -> Completer
31+
contextToCompleter :: Context -> CabalCompleter
3232
-- if we are in the top level of the cabal file and not in a keyword context,
3333
-- we can write any top level keywords or a stanza declaration
3434
contextToCompleter (TopLevel, None) =

plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/Completion/Data.hs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import Ide.Plugin.Cabal.Completion.Completer.FilePath
1212
import Ide.Plugin.Cabal.Completion.Completer.Module
1313
import Ide.Plugin.Cabal.Completion.Completer.Paths
1414
import Ide.Plugin.Cabal.Completion.Completer.Simple
15-
import Ide.Plugin.Cabal.Completion.Completer.Types (Completer)
15+
import Ide.Plugin.Cabal.Completion.Completer.Types (CabalCompleter)
1616
import Ide.Plugin.Cabal.Completion.Types
1717
import Ide.Plugin.Cabal.LicenseSuggest (licenseNames)
1818

@@ -35,7 +35,7 @@ supportedCabalVersions :: [CabalSpecVersion]
3535
supportedCabalVersions = [CabalSpecV2_2 .. maxBound]
3636

3737
-- | Keyword for cabal version; required to be the top line in a cabal file
38-
cabalVersionKeyword :: Map KeyWordName Completer
38+
cabalVersionKeyword :: Map KeyWordName CabalCompleter
3939
cabalVersionKeyword =
4040
Map.singleton "cabal-version:" $
4141
constantCompleter $
@@ -47,7 +47,7 @@ cabalVersionKeyword =
4747
--
4848
-- TODO: we could add descriptions of field values and
4949
-- then show them when inside the field's context
50-
cabalKeywords :: Map KeyWordName Completer
50+
cabalKeywords :: Map KeyWordName CabalCompleter
5151
cabalKeywords =
5252
Map.fromList
5353
[ ("name:", nameCompleter),
@@ -76,7 +76,7 @@ cabalKeywords =
7676

7777
-- | Map, containing all stanzas in a cabal file as keys,
7878
-- and lists of their possible nested keywords as values.
79-
stanzaKeywordMap :: Map StanzaType (Map KeyWordName Completer)
79+
stanzaKeywordMap :: Map StanzaType (Map KeyWordName CabalCompleter)
8080
stanzaKeywordMap =
8181
Map.fromList
8282
[ ("library", libraryFields <> libExecTestBenchCommons Library),
@@ -90,7 +90,7 @@ stanzaKeywordMap =
9090
("source-repository", sourceRepositoryFields)
9191
]
9292

93-
libraryFields :: Map KeyWordName Completer
93+
libraryFields :: Map KeyWordName CabalCompleter
9494
libraryFields =
9595
Map.fromList
9696
[ ("exposed-modules:", modulesCompleter sourceDirsExtractionLibrary),
@@ -102,31 +102,31 @@ libraryFields =
102102
("other-modules:", modulesCompleter sourceDirsExtractionLibrary)
103103
]
104104

105-
executableFields :: Map KeyWordName Completer
105+
executableFields :: Map KeyWordName CabalCompleter
106106
executableFields =
107107
Map.fromList
108108
[ ("main-is:", mainIsCompleter sourceDirsExtractionExecutable),
109109
("scope:", constantCompleter ["public", "private"]),
110110
("other-modules:", modulesCompleter sourceDirsExtractionExecutable)
111111
]
112112

113-
testSuiteFields :: Map KeyWordName Completer
113+
testSuiteFields :: Map KeyWordName CabalCompleter
114114
testSuiteFields =
115115
Map.fromList
116116
[ ("type:", constantCompleter ["exitcode-stdio-1.0", "detailed-0.9"]),
117117
("main-is:", mainIsCompleter sourceDirsExtractionTestSuite),
118118
("other-modules:", modulesCompleter sourceDirsExtractionTestSuite)
119119
]
120120

121-
benchmarkFields :: Map KeyWordName Completer
121+
benchmarkFields :: Map KeyWordName CabalCompleter
122122
benchmarkFields =
123123
Map.fromList
124124
[ ("type:", noopCompleter),
125125
("main-is:", mainIsCompleter sourceDirsExtractionBenchmark),
126126
("other-modules:", modulesCompleter sourceDirsExtractionBenchmark)
127127
]
128128

129-
foreignLibraryFields :: Map KeyWordName Completer
129+
foreignLibraryFields :: Map KeyWordName CabalCompleter
130130
foreignLibraryFields =
131131
Map.fromList
132132
[ ("type:", constantCompleter ["native-static", "native-shared"]),
@@ -136,7 +136,7 @@ foreignLibraryFields =
136136
("lib-version-linux:", noopCompleter)
137137
]
138138

139-
sourceRepositoryFields :: Map KeyWordName Completer
139+
sourceRepositoryFields :: Map KeyWordName CabalCompleter
140140
sourceRepositoryFields =
141141
Map.fromList
142142
[ ( "type:",
@@ -160,7 +160,7 @@ sourceRepositoryFields =
160160
("subdir:", directoryCompleter)
161161
]
162162

163-
flagFields :: Map KeyWordName Completer
163+
flagFields :: Map KeyWordName CabalCompleter
164164
flagFields =
165165
Map.fromList
166166
[ ("description:", noopCompleter),
@@ -171,7 +171,7 @@ flagFields =
171171
("lib-version-linux:", noopCompleter)
172172
]
173173

174-
libExecTestBenchCommons :: TopLevelStanza -> Map KeyWordName Completer
174+
libExecTestBenchCommons :: TopLevelStanza -> Map KeyWordName CabalCompleter
175175
libExecTestBenchCommons st =
176176
Map.fromList
177177
[ ("import:", importCompleter),

plugins/hls-cabal-project-plugin/src/Ide/Plugin/CabalProject.hs

Lines changed: 31 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -9,45 +9,45 @@ module Ide.Plugin.CabalProject where
99

1010
import Control.Concurrent.Strict
1111
import Control.DeepSeq
12-
import Control.Lens ((^.))
12+
import Control.Lens ((^.))
1313
import Control.Monad.Extra
1414
import Control.Monad.IO.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
1717
import Data.Hashable
18-
import Data.HashMap.Strict (HashMap)
18+
import Data.HashMap.Strict (HashMap)
1919
-- toList)
20-
import qualified Data.HashMap.Strict as HashMap
21-
import qualified Data.List.NonEmpty as NE
20+
import qualified Data.HashMap.Strict as HashMap
21+
import qualified Data.List.NonEmpty as NE
2222
import Data.Proxy
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
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
3434
-- import Distribution.PackageDescription (allBuildDepends,
3535
-- depPkgName,
3636
-- unPackageName)
37-
import qualified Distribution.Parsec.Position as Syntax
37+
import qualified Distribution.Parsec.Position as Syntax
3838
import GHC.Generics
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
39+
import qualified Ide.Plugin.Cabal.Completion.Types as CTypes
40+
import Ide.Plugin.Cabal.Orphans ()
41+
import qualified Ide.Plugin.CabalProject.Completion.Completer.Types as CPCompleterTypes
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
4646
import Ide.Types
47-
import qualified Language.LSP.Protocol.Lens as JL
48-
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
4949
import Language.LSP.Protocol.Types
50-
import qualified Language.LSP.VFS as VFS
50+
import qualified Language.LSP.VFS as VFS
5151

5252
data Log
5353
= LogModificationTime NormalizedFilePath FileVersion
@@ -324,12 +324,10 @@ computeCompletionsAt recorder _ prefInfo _ fields = do
324324
Just ctx -> do
325325
logWith recorder Debug $ LogCompletionContext ctx pos
326326
let completer = Completions.contextToCompleter ctx
327-
let completerData = CompleterTypes.CompleterData
327+
let completerData = CPCompleterTypes.CabalProjectCompleterData
328328
{
329-
getLatestGPD = pure Nothing,
330-
getCabalCommonSections = pure Nothing,
331-
cabalPrefixInfo = prefInfo
332-
, stanzaName =
329+
cabalProjectPrefixInfo = prefInfo
330+
, cabalProjectStanzaName =
333331
case fst ctx of
334332
CTypes.Stanza _ name -> name
335333
_ -> Nothing
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{-# LANGUAGE TypeFamilies #-}
2+
3+
module Ide.Plugin.CabalProject.Completion.Completer.Types where
4+
5+
import Ide.Plugin.Cabal.Completion.Completer.Types
6+
import Ide.Plugin.Cabal.Completion.Types
7+
8+
-- Cabal.project specific completer
9+
type CabalProjectCompleter = Completer CabalProjectCompleterData
10+
11+
-- | Contains information to be used by completers.
12+
data CabalProjectCompleterData = CabalProjectCompleterData
13+
{
14+
-- | Prefix info to be used for constructing completion items
15+
cabalProjectPrefixInfo :: CabalPrefixInfo,
16+
-- | The name of the stanza in which the completer is applied
17+
cabalProjectStanzaName :: Maybe StanzaName
18+
}
19+
20+
-- Allows CabalProjectCompleter to be used by Completers
21+
instance HasPrefixInfo CabalProjectCompleterData where
22+
getPrefixInfo = cabalProjectPrefixInfo

0 commit comments

Comments
 (0)