@@ -43,6 +43,10 @@ import qualified Data.Map as Map
43
43
import Language.LSP.VFS (VirtualFile )
44
44
import qualified Data.Text.Utf16.Rope as Rope
45
45
import qualified Data.List as List
46
+ import qualified Language.LSP.Types.Lens as Map
47
+ import Development.IDE.GHC.Compat (getContext )
48
+ import Debug.Trace
49
+ import qualified Data.List.Extra as Extra
46
50
data Log
47
51
= LogModificationTime NormalizedFilePath (Maybe FileVersion )
48
52
| LogDiagnostics NormalizedFilePath [FileDiagnostic ]
@@ -72,7 +76,7 @@ instance Pretty Log where
72
76
descriptor :: Recorder (WithPriority Log ) -> PluginId -> PluginDescriptor IdeState
73
77
descriptor recorder plId = (defaultCabalPluginDescriptor plId)
74
78
{ pluginRules = cabalRules recorder
75
- , pluginHandlers = mkPluginHandler STextDocumentCodeAction licenseSuggestCodeAction
79
+ , pluginHandlers = mkPluginHandler STextDocumentCodeAction licenseSuggestCodeAction
76
80
<> mkPluginHandler J. STextDocumentCompletion completion
77
81
, pluginNotificationHandlers = mconcat
78
82
[ mkPluginNotificationHandler LSP. STextDocumentDidOpen $
@@ -166,6 +170,7 @@ licenseSuggestCodeAction _ _ (CodeActionParams _ _ (TextDocumentIdentifier uri)
166
170
-- ----------------------------------------------------------------
167
171
-- Completion
168
172
-- ----------------------------------------------------------------
173
+
169
174
completion :: PluginMethodHandler IdeState 'J.TextDocumentCompletion
170
175
completion _ide _ complParams = do
171
176
let (J. TextDocumentIdentifier uri) = complParams ^. JL. textDocument
@@ -180,103 +185,150 @@ completion _ide _ complParams = do
180
185
result :: Maybe VFS. PosPrefixInfo -> VirtualFile -> J. List CompletionItem
181
186
result Nothing _ = J. List []
182
187
result (Just pfix) cnts
183
- | (VFS. cursorPos pfix) ^. JL. line == 0 = J. List [buildCompletion cabalVersionKeyword]
184
- | Stanza s <- findCurrentLevel (getPreviousLines pfix cnts) =
185
- case (Map. lookup s stanzaKeywordMap) of
186
- Nothing ->
187
- J. List $
188
- makeCompletionItems pfix topLevelKeywords
189
- Just l -> J. List $ (makeCompletionItems pfix l) ++ (makeCompletionItems pfix $ Map. keys stanzaKeywordMap)
190
- | otherwise =
191
- J. List $
192
- makeCompletionItems pfix topLevelKeywords
193
- where
194
- topLevelKeywords = cabalKeywords ++ Map. keys stanzaKeywordMap
195
-
196
- -- | Takes info about the current cursor position and a set of possible keywords
188
+ | pos ^. JL. line == 0 = J. List [buildCompletion (fst cabalVersionKeyword)]
189
+ | Just ctx@ (Stanza s kwContext) <- context =
190
+ case (Map. lookup s stanzaKeywordMap) of
191
+ Nothing ->
192
+ case kwContext of
193
+ None ->
194
+ J. List $
195
+ makeCompletionItems pfix topLevelKeywords
196
+ KeyWord kw -> J. List $ makeCompletionItems pfix (getPossibleValuesForKeyWord kw ctx)
197
+ Just m ->
198
+ case kwContext of
199
+ None -> J. List $ (makeCompletionItems pfix (Map. keys m)) ++ (makeCompletionItems pfix $ Map. keys stanzaKeywordMap)
200
+ KeyWord kw -> J. List $ makeCompletionItems pfix (getPossibleValuesForKeyWord kw ctx)
201
+ | Just ctx@ (TopLevel kwContext) <- context =
202
+ case kwContext of
203
+ None -> J. List $ makeCompletionItems pfix topLevelKeywords
204
+ KeyWord kw -> J. List $ makeCompletionItems pfix (getPossibleValuesForKeyWord kw ctx)
205
+ | otherwise = J. List []
206
+ where
207
+ pos = VFS. cursorPos pfix
208
+ topLevelKeywords = Map. keys cabalKeywords ++ Map. keys stanzaKeywordMap
209
+ context = findCurrentContext pos (cnts ^. VFS. file_text)
210
+
211
+ getPossibleValuesForKeyWord :: T. Text -> Context -> [T. Text ]
212
+ getPossibleValuesForKeyWord kw (TopLevel _) =
213
+ case Map. lookup kw cabalKeywords of
214
+ Nothing -> []
215
+ Just l -> l
216
+ getPossibleValuesForKeyWord kw (Stanza s _) =
217
+ case Map. lookup s stanzaKeywordMap of
218
+ Nothing -> []
219
+ Just m -> case Map. lookup kw m of
220
+ Nothing -> []
221
+ Just l -> l
222
+
223
+ findCurrentContext :: Position -> Rope. Rope -> Maybe Context
224
+ findCurrentContext pos rope =
225
+ case outerContext of
226
+ TopLevel _ -> TopLevel <$> getKeyWordContext cabalKeywords
227
+ Stanza s _ ->
228
+ case traceShowId (Map. lookup (traceShowId s) stanzaKeywordMap) of
229
+ Nothing -> pure $ Stanza s None
230
+ Just m -> traceShowId $ Stanza s <$> getKeyWordContext m
231
+ where
232
+ outerContext = findCurrentLevel (getPreviousLines pos rope) None
233
+ currentLine = traceShowId $ (Rope. lines rope) Extra. !? (fromIntegral $ pos ^. JL. line)
234
+ getKeyWordContext keywords = do
235
+ curLine <- fmap T. stripStart currentLine
236
+ case List. find (\ kw -> traceShowId kw `T.isPrefixOf` curLine) (traceShowId $ Map. keys keywords) of
237
+ Nothing -> Just None
238
+ Just kw -> Just $ KeyWord kw
239
+
240
+ -- | Takes info about the current cursor position and a set of possible keywords
197
241
-- and creates completion suggestions that fit the current input from the given list
198
242
makeCompletionItems :: VFS. PosPrefixInfo -> [T. Text ] -> [CompletionItem ]
199
243
makeCompletionItems pfix l =
200
244
map
201
245
(buildCompletion . Fuzzy. original)
202
246
(Fuzzy. simpleFilter 1000 10 (VFS. prefixText pfix) l)
203
247
204
- -- | Parse the given set of lines (starting before current cursor position
205
- -- up to the start of the file) to find the nearest stanza declaration,
248
+ -- | Parse the given set of lines (starting before current cursor position
249
+ -- up to the start of the file) to find the nearest stanza declaration,
206
250
-- if none is found we are in the top level
207
- findCurrentLevel :: [T. Text ] -> Context
208
- findCurrentLevel [] = TopLevel
209
- findCurrentLevel (cur : xs)
210
- | Just s <- stanza = Stanza s
211
- | otherwise = findCurrentLevel xs
251
+ findCurrentLevel :: [T. Text ] -> KeyWordContext -> Context
252
+ findCurrentLevel [] kwContext = TopLevel kwContext
253
+ findCurrentLevel (cur : xs) kwContext
254
+ | Just s <- stanza = Stanza s kwContext
255
+ | otherwise = findCurrentLevel xs kwContext
212
256
where
213
257
stanza = List. find (`T.isPrefixOf` cur) (Map. keys stanzaKeywordMap)
214
258
215
- -- | Get all lines before the given cursor position in the given file
259
+ -- | Get all lines before the given cursor position in the given file
216
260
-- and reverse them since we want to traverse starting from our current position
217
- getPreviousLines :: VFS. PosPrefixInfo -> VirtualFile -> [T. Text ]
218
- getPreviousLines pos cont = reverse $ take (fromIntegral currentLine) allLines
261
+ getPreviousLines :: Position -> Rope. Rope -> [T. Text ]
262
+ getPreviousLines pos rope = reverse $ take (fromIntegral currentLine) allLines
219
263
where
220
- allLines = Rope. lines $ cont ^. VFS. file_text
221
- currentLine = ( VFS. cursorPos pos) ^. JL. line
264
+ allLines = Rope. lines rope
265
+ currentLine = pos ^. JL. line
222
266
223
-
224
- data Context
225
- = TopLevel
267
+ data Context
268
+ = TopLevel KeyWordContext
226
269
-- ^ top level context in a cabal file such as 'author'
227
- | Stanza T. Text
270
+ | Stanza T. Text KeyWordContext
228
271
-- ^ nested context in a cabal file, such as 'library', which has nested keywords, specific to the stanza
229
- deriving (Eq )
272
+ deriving (Eq , Show )
273
+
274
+ -- | Keyword context in cabal file
275
+ data KeyWordContext
276
+ = KeyWord T. Text
277
+ -- ^ we are in a line with the given keyword before our cursor
278
+ | None
279
+ -- ^ we are in a line with no keyword context
280
+ deriving (Eq , Show )
230
281
231
282
-- | Keyword for cabal version required to be the top line in a cabal file
232
- cabalVersionKeyword :: T. Text
233
- cabalVersionKeyword = " cabal-version:"
283
+ cabalVersionKeyword :: ( T. Text,[ T. Text ])
284
+ cabalVersionKeyword = ( " cabal-version:" , [] )
234
285
235
286
-- | Top level keywords of a cabal file
236
- cabalKeywords :: [T. Text ]
237
- cabalKeywords =
238
- [
239
- " name:" ,
240
- " version:" ,
241
- " build-type:" ,
242
- " license:" ,
243
- " license-file:" ,
244
- " license-files:" ,
245
- " copyright:" ,
246
- " author:" ,
247
- " maintainer:" ,
248
- " stability:" ,
249
- " homepage:" ,
250
- " bug-reports:" ,
251
- " package-url:" ,
252
- " synopsis:" ,
253
- " description:" ,
254
- " category:" ,
255
- " tested-with:" ,
256
- " data-files:" ,
257
- " data-dir:" ,
258
- " data-dir:" ,
259
- " extra-doc-files:" ,
260
- " extra-tmp-files:"
287
+ cabalKeywords :: Map T. Text [T. Text ]
288
+ cabalKeywords =
289
+ Map. fromList [
290
+ ( " name:" , [] ) ,
291
+ ( " version:" , [] ) ,
292
+ ( " build-type:" , [ " Simple " , " Custom " ]) ,
293
+ ( " license:" , [ " NONE " ]) ,
294
+ ( " license-file:" , [] ) ,
295
+ ( " license-files:" , [] ) ,
296
+ ( " copyright:" , [] ) ,
297
+ ( " author:" , [] )
298
+ -- "maintainer:",
299
+ -- "stability:",
300
+ -- "homepage:",
301
+ -- "bug-reports:",
302
+ -- "package-url:",
303
+ -- "synopsis:",
304
+ -- "description:",
305
+ -- "category:",
306
+ -- "tested-with:",
307
+ -- "data-files:",
308
+ -- "data-dir:",
309
+ -- "data-dir:",
310
+ -- "extra-doc-files:",
311
+ -- "extra-tmp-files:"
261
312
]
262
313
263
314
-- | Map, containing all stanzas in a cabal file as keys and lists of their possible nested keywords as values
264
- stanzaKeywordMap :: Map T. Text [T. Text ]
265
- stanzaKeywordMap = Map. fromList [(" library" , [
266
- " exposed-modules:" ,
267
- " virtual-modules:" ,
268
- " exposed:" ,
269
- " visibility:" ,
270
- " reexported-modules:" ,
271
- " signatures:"
272
- ])]
273
-
315
+ stanzaKeywordMap :: Map T. Text (Map T. Text [T. Text ])
316
+ stanzaKeywordMap = Map. fromList [(" library" , Map. fromList[
317
+ (" exposed-modules:" , [] ),
318
+ (" virtual-modules:" , [] ),
319
+ (" exposed:" , [" True" , " False" ]),
320
+ (" visibility:" , [" private" , " public" ]),
321
+ (" reexported-modules:" , [] ),
322
+ (" signatures:" , [] )
323
+ ]),
324
+ (" test-suite" , Map. fromList[] )
325
+ ]
326
+
274
327
275
328
-- TODO move out toplevel commands i.e. test-suite
276
329
-- cabalTestKeywords :: [T.Text]
277
- -- cabalTestKeywords =
330
+ -- cabalTestKeywords =
278
331
-- [
279
- -- "test-suite",
280
332
-- "type:",
281
333
-- "main-is:",
282
334
-- "test-module:",
@@ -336,7 +388,7 @@ stanzaKeywordMap = Map.fromList [("library", [
336
388
-- ]
337
389
338
390
-- cabalFlagKeywords :: [(T.Text, T.Text)]
339
- -- cabalFlagKeywords =
391
+ -- cabalFlagKeywords =
340
392
-- [
341
393
-- ("flag", "name"),
342
394
-- ("description:", "freeform"),
@@ -345,14 +397,14 @@ stanzaKeywordMap = Map.fromList [("library", [
345
397
-- ]
346
398
347
399
-- cabalStanzaKeywords :: [(T.Text, T.Text)]
348
- -- cabalStanzaKeywords =
400
+ -- cabalStanzaKeywords =
349
401
-- [
350
402
-- ("common", "name"),
351
403
-- ("import:", "token-list")
352
404
-- ]
353
405
354
406
-- cabalSourceRepoKeywords :: [(T.Text, T.Text)]
355
- -- cabalSourceRepoKeywords =
407
+ -- cabalSourceRepoKeywords =
356
408
-- [
357
409
-- ("source-repository", ""),
358
410
-- ("type:", "token"),
0 commit comments