@@ -18,25 +18,31 @@ module Ide.Plugin.Pragmas
1818import Control.Lens hiding (List )
1919import Control.Monad.IO.Class (MonadIO (liftIO ))
2020import Control.Monad.Trans.Class (lift )
21+ import Data.Char (isAlphaNum )
2122import Data.List.Extra (nubOrdOn )
2223import qualified Data.Map as M
23- import Data.Maybe (mapMaybe )
24+ import Data.Maybe (fromMaybe ,
25+ listToMaybe ,
26+ mapMaybe )
2427import qualified Data.Text as T
28+ import qualified Data.Text.Utf16.Rope.Mixed as Rope
2529import Development.IDE hiding (line )
2630import Development.IDE.Core.Compile (sourceParser ,
2731 sourceTypecheck )
2832import Development.IDE.Core.PluginUtils
2933import Development.IDE.GHC.Compat
3034import Development.IDE.Plugin.Completions (ghcideCompletionsPluginPriority )
3135import Development.IDE.Plugin.Completions.Logic (getCompletionPrefix )
32- import Development.IDE.Plugin.Completions.Types (PosPrefixInfo (.. ))
36+ import Development.IDE.Plugin.Completions.Types (PosPrefixInfo (.. ),
37+ prefixText )
3338import qualified Development.IDE.Spans.Pragmas as Pragmas
3439import Ide.Plugin.Error
3540import Ide.Types
3641import qualified Language.LSP.Protocol.Lens as L
3742import qualified Language.LSP.Protocol.Message as LSP
3843import qualified Language.LSP.Protocol.Types as LSP
3944import qualified Language.LSP.Server as LSP
45+ import qualified Language.LSP.VFS as VFS
4046import qualified Text.Fuzzy as Fuzzy
4147
4248-- ---------------------------------------------------------------------
@@ -193,30 +199,32 @@ allPragmas =
193199
194200-- ---------------------------------------------------------------------
195201flags :: [T. Text ]
196- flags = map ( T. pack . stripLeading ' - ' ) $ flagsForCompletion False
202+ flags = map T. pack $ flagsForCompletion False
197203
198204completion :: PluginMethodHandler IdeState 'LSP.Method_TextDocumentCompletion
199205completion _ide _ complParams = do
200206 let (LSP. TextDocumentIdentifier uri) = complParams ^. L. textDocument
201- position = complParams ^. L. position
207+ cursorPos @ ( Position l c) = complParams ^. L. position
202208 contents <- lift $ LSP. getVirtualFile $ toNormalizedUri uri
203209 fmap LSP. InL $ case (contents, uriToFilePath' uri) of
204210 (Just cnts, Just _path) ->
205- pure $ result $ getCompletionPrefix position cnts
211+ pure $ result $ getCompletionPrefix cursorPos cnts
206212 where
207213 result pfix
208214 | " {-# language" `T.isPrefixOf` line
209- = map buildCompletion
210- ( Fuzzy. simpleFilter (prefixText pfix) allPragmas)
215+ = map mkLanguagePragmaCompl $
216+ Fuzzy. simpleFilter (prefixText pfix) allPragmas
211217 | " {-# options_ghc" `T.isPrefixOf` line
212- = map buildCompletion
213- (Fuzzy. simpleFilter (prefixText pfix) flags)
218+ = let flagPrefix = getGhcOptionPrefix cursorPos cnts
219+ prefixLength = fromIntegral $ T. length flagPrefix
220+ prefixRange = LSP. Range (Position l (c - prefixLength)) cursorPos
221+ in map (mkGhcOptionCompl prefixRange) $ Fuzzy. simpleFilter flagPrefix flags
214222 | " {-#" `T.isPrefixOf` line
215223 = [ mkPragmaCompl (a <> suffix) b c
216224 | (a, b, c, w) <- validPragmas, w == NewLine
217225 ]
218226 | -- Do not suggest any pragmas any of these conditions:
219- -- 1. Current line is a an import
227+ -- 1. Current line is an import
220228 -- 2. There is a module name right before the current word.
221229 -- Something like `Text.la` shouldn't suggest adding the
222230 -- 'LANGUAGE' pragma.
@@ -239,7 +247,7 @@ completion _ide _ complParams = do
239247 module_ = prefixScope pfix
240248 word = prefixText pfix
241249 -- Not completely correct, may fail if more than one "{-#" exist
242- -- , we can ignore it since it rarely happen .
250+ -- , we can ignore it since it rarely happens .
243251 prefix
244252 | " {-# " `T.isInfixOf` line = " "
245253 | " {-#" `T.isInfixOf` line = " "
@@ -293,19 +301,43 @@ mkPragmaCompl insertText label detail =
293301 Nothing Nothing Nothing Nothing Nothing (Just insertText) (Just LSP. InsertTextFormat_Snippet )
294302 Nothing Nothing Nothing Nothing Nothing Nothing Nothing
295303
296-
297- stripLeading :: Char -> String -> String
298- stripLeading _ [] = []
299- stripLeading c (s: ss)
300- | s == c = ss
301- | otherwise = s: ss
302-
303-
304- buildCompletion :: T. Text -> LSP. CompletionItem
305- buildCompletion label =
304+ getGhcOptionPrefix :: Position -> VFS. VirtualFile -> T. Text
305+ getGhcOptionPrefix (Position l c) (VFS. VirtualFile _ _ ropetext) =
306+ fromMaybe " " $ do
307+ let lastMaybe = listToMaybe . reverse
308+
309+ -- grab the entire line the cursor is at
310+ curLine <- listToMaybe
311+ $ Rope. lines
312+ $ fst $ Rope. splitAtLine 1
313+ $ snd $ Rope. splitAtLine (fromIntegral l) ropetext
314+ let beforePos = T. take (fromIntegral c) curLine
315+ -- the word getting typed, after previous space and before cursor
316+ curWord <-
317+ if | T. null beforePos -> Just " "
318+ | T. last beforePos == ' ' -> Just " " -- don't count abc as the curword in 'abc '
319+ | otherwise -> lastMaybe (T. words beforePos)
320+ pure $ T. takeWhileEnd isGhcOptionChar curWord
321+
322+ -- | Is this character contained in some GHC flag? Based on:
323+ -- GHCi> nub . sort . concat $ GHC.Driver.Session.flagsForCompletion False
324+ -- "#-.01234589=ABCDEFGHIJKLMNOPQRSTUVWX_abcdefghijklmnopqrstuvwxyz"
325+ isGhcOptionChar :: Char -> Bool
326+ isGhcOptionChar c = isAlphaNum c || c `elem` (" #-.=_" :: String )
327+
328+ mkLanguagePragmaCompl :: T. Text -> LSP. CompletionItem
329+ mkLanguagePragmaCompl label =
306330 LSP. CompletionItem label Nothing (Just LSP. CompletionItemKind_Keyword ) Nothing Nothing
307331 Nothing Nothing Nothing Nothing Nothing Nothing Nothing
308332 Nothing Nothing Nothing Nothing Nothing Nothing Nothing
309333
334+ mkGhcOptionCompl :: Range -> T. Text -> LSP. CompletionItem
335+ mkGhcOptionCompl editRange completedFlag =
336+ LSP. CompletionItem completedFlag Nothing (Just LSP. CompletionItemKind_Keyword ) Nothing Nothing
337+ Nothing Nothing Nothing Nothing Nothing Nothing Nothing
338+ Nothing (Just insertCompleteFlag) Nothing Nothing Nothing Nothing Nothing
339+ where
340+ insertCompleteFlag = LSP. InL $ LSP. TextEdit editRange completedFlag
341+
310342
311343
0 commit comments