Skip to content

Commit 8ae29e3

Browse files
committed
hls-pragmas-plugin: Reduce noisy completions
1 parent c126332 commit 8ae29e3

File tree

3 files changed

+83
-22
lines changed

3 files changed

+83
-22
lines changed

plugins/hls-pragmas-plugin/hls-pragmas-plugin.cabal

+1
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ test-suite tests
4949
main-is: Main.hs
5050
ghc-options: -threaded -rtsopts -with-rtsopts=-N
5151
build-depends:
52+
, aeson
5253
, base
5354
, filepath
5455
, hls-pragmas-plugin

plugins/hls-pragmas-plugin/src/Ide/Plugin/Pragmas.hs

+18-12
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ module Ide.Plugin.Pragmas
1212
( descriptor
1313
-- For testing
1414
, validPragmas
15+
, AppearWhere(..)
1516
) where
1617

1718
import Control.Lens hiding (List)
@@ -181,23 +182,32 @@ completion _ide _ complParams = do
181182
contents <- LSP.getVirtualFile $ toNormalizedUri uri
182183
fmap (Right . J.InL) $ case (contents, uriToFilePath' uri) of
183184
(Just cnts, Just _path) ->
184-
result <$> VFS.getCompletionPrefix position cnts
185+
J.List . result <$> VFS.getCompletionPrefix position cnts
185186
where
186187
result (Just pfix)
187188
| "{-# language" `T.isPrefixOf` line
188-
= J.List $ map buildCompletion
189+
= map buildCompletion
189190
(Fuzzy.simpleFilter (VFS.prefixText pfix) allPragmas)
190191
| "{-# options_ghc" `T.isPrefixOf` line
191-
= J.List $ map buildCompletion
192+
= map buildCompletion
192193
(Fuzzy.simpleFilter (VFS.prefixText pfix) flags)
193194
| "{-#" `T.isPrefixOf` line
194-
= J.List $ [ mkPragmaCompl (a <> suffix) b c
195-
| (a, b, c, w) <- validPragmas, w == NewLine ]
195+
= [ mkPragmaCompl (a <> suffix) b c
196+
| (a, b, c, w) <- validPragmas, w == NewLine
197+
]
198+
| "import" `T.isPrefixOf` line || not (T.null module_) || T.null word
199+
= []
196200
| otherwise
197-
= J.List $ [ mkPragmaCompl (prefix <> a <> suffix) b c
198-
| (a, b, c, _) <- validPragmas, Fuzzy.test word b]
201+
= [ mkPragmaCompl (prefix <> pragmaTemplate <> suffix) matcher detail
202+
| (pragmaTemplate, matcher, detail, appearWhere) <- validPragmas
203+
, Fuzzy.test word matcher
204+
, (appearWhere == NewLine && line == word)
205+
|| (appearWhere == CanInline && line /= word)
206+
|| (T.elem ' ' matcher && appearWhere == NewLine && Fuzzy.test word matcher)
207+
]
199208
where
200209
line = T.toLower $ VFS.fullLine pfix
210+
module_ = VFS.prefixModule pfix
201211
word = VFS.prefixText pfix
202212
-- Not completely correct, may fail if more than one "{-#" exist
203213
-- , we can ignore it since it rarely happen.
@@ -211,9 +221,8 @@ completion _ide _ complParams = do
211221
| "-}" `T.isSuffixOf` line = " #"
212222
| "}" `T.isSuffixOf` line = " #-"
213223
| otherwise = " #-}"
214-
result Nothing = J.List []
224+
result Nothing = []
215225
_ -> return $ J.List []
216-
217226
-----------------------------------------------------------------------
218227

219228
-- | Pragma where exist
@@ -268,6 +277,3 @@ buildCompletion label =
268277
J.CompletionItem label (Just J.CiKeyword) Nothing Nothing
269278
Nothing Nothing Nothing Nothing Nothing Nothing Nothing
270279
Nothing Nothing Nothing Nothing Nothing Nothing
271-
272-
273-

plugins/hls-pragmas-plugin/test/Main.hs

+64-10
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ module Main
55
) where
66

77
import Control.Lens ((<&>), (^.))
8+
import Data.Aeson
9+
import Data.Foldable
810
import qualified Data.Text as T
911
import Ide.Plugin.Pragmas
1012
import qualified Language.LSP.Types.Lens as L
@@ -25,6 +27,7 @@ tests =
2527
, codeActionTests'
2628
, completionTests
2729
, completionSnippetTests
30+
, dontSuggestCompletionTests
2831
]
2932

3033
codeActionTests :: TestTree
@@ -127,29 +130,80 @@ completionSnippetTests :: TestTree
127130
completionSnippetTests =
128131
testGroup "expand snippet to pragma" $
129132
validPragmas <&>
130-
(\(insertText, label, detail, _) ->
131-
let input = T.toLower $ T.init label
133+
(\(insertText, label, detail, appearWhere) ->
134+
let inputPrefix =
135+
case appearWhere of
136+
NewLine -> ""
137+
CanInline -> "something "
138+
input = inputPrefix <> (T.toLower $ T.init label)
132139
in completionTest (T.unpack label)
133140
"Completion.hs" input label (Just Snippet)
134141
(Just $ "{-# " <> insertText <> " #-}") (Just detail)
135142
[0, 0, 0, 34, 0, fromIntegral $ T.length input])
136143

137-
completionTest :: String -> String -> T.Text -> T.Text -> Maybe InsertTextFormat -> Maybe T.Text -> Maybe T.Text -> [UInt] -> TestTree
138-
completionTest testComment fileName te' label textFormat insertText detail [a, b, c, d, x, y] =
144+
dontSuggestCompletionTests :: TestTree
145+
dontSuggestCompletionTests =
146+
testGroup "do not suggest pragmas" $
147+
let replaceFuncBody newBody = Just $ mkEdit (8,6) (8,8) newBody
148+
writeInEmptyLine txt = Just $ mkEdit (3,0) (3,0) txt
149+
generalTests = [ provideNoCompletionsTest "in imports" "Completion.hs" (Just $ mkEdit (3,0) (3,0) "import WA") (Position 3 8)
150+
, provideNoCompletionsTest "when no word has been typed" "Completion.hs" Nothing (Position 3 0)
151+
, provideNoCompletionsTest "when expecting auto complete on modules" "Completion.hs" (Just $ mkEdit (8,6) (8,8) "Data.Maybe.WA") (Position 8 19)
152+
]
153+
individualPragmaTests = validPragmas <&> \(insertText,label,detail,appearWhere) ->
154+
let completionPrompt = T.toLower $ T.init label
155+
promptLen = fromIntegral (T.length completionPrompt)
156+
in case appearWhere of
157+
CanInline ->
158+
provideNoUndesiredCompletionsTest ("at new line: " <> T.unpack label) "Completion.hs" (Just label) (writeInEmptyLine completionPrompt) (Position 3 0)
159+
NewLine ->
160+
provideNoUndesiredCompletionsTest ("inline: " <> T.unpack label) "Completion.hs" (Just label) (replaceFuncBody completionPrompt) (Position 8 (6 + promptLen))
161+
in generalTests ++ individualPragmaTests
162+
163+
mkEdit :: (UInt,UInt) -> (UInt,UInt) -> T.Text -> TextEdit
164+
mkEdit (startLine, startCol) (endLine, endCol) newText =
165+
TextEdit (Range (Position startLine startCol) (Position endLine endCol)) newText
166+
167+
completionTest :: String -> FilePath -> T.Text -> T.Text -> Maybe InsertTextFormat -> Maybe T.Text -> Maybe T.Text -> [UInt] -> TestTree
168+
completionTest testComment fileName replacementText expectedLabel expectedFormat expectedInsertText detail [delFromLine, delFromCol, delToLine, delToCol, completeAtLine, completeAtCol] =
139169
testCase testComment $ runSessionWithServer pragmasPlugin testDataDir $ do
140170
doc <- openDoc fileName "haskell"
141171
_ <- waitForDiagnostics
142-
let te = TextEdit (Range (Position a b) (Position c d)) te'
172+
let te = TextEdit (Range (Position delFromLine delFromCol) (Position delToLine delToCol)) replacementText
143173
_ <- applyEdit doc te
144-
compls <- getCompletions doc (Position x y)
145-
item <- getCompletionByLabel label compls
174+
compls <- getCompletions doc (Position completeAtLine completeAtCol)
175+
item <- getCompletionByLabel expectedLabel compls
146176
liftIO $ do
147-
item ^. L.label @?= label
177+
item ^. L.label @?= expectedLabel
148178
item ^. L.kind @?= Just CiKeyword
149-
item ^. L.insertTextFormat @?= textFormat
150-
item ^. L.insertText @?= insertText
179+
item ^. L.insertTextFormat @?= expectedFormat
180+
item ^. L.insertText @?= expectedInsertText
151181
item ^. L.detail @?= detail
152182

183+
provideNoCompletionsTest :: String -> FilePath -> Maybe TextEdit -> Position -> TestTree
184+
provideNoCompletionsTest testComment fileName mTextEdit pos =
185+
provideNoUndesiredCompletionsTest testComment fileName Nothing mTextEdit pos
186+
187+
provideNoUndesiredCompletionsTest :: String -> FilePath -> Maybe T.Text -> Maybe TextEdit -> Position -> TestTree
188+
provideNoUndesiredCompletionsTest testComment fileName mUndesiredLabel mTextEdit pos =
189+
testCase testComment $ runSessionWithServer pragmasPlugin testDataDir $ do
190+
doc <- openDoc fileName "haskell"
191+
_ <- waitForDiagnostics
192+
_ <- sendConfigurationChanged disableGhcideCompletions
193+
mapM_ (applyEdit doc) mTextEdit
194+
compls <- getCompletions doc pos
195+
liftIO $ case mUndesiredLabel of
196+
Nothing -> compls @?= []
197+
Just undesiredLabel -> do
198+
case find (\c -> c ^. L.label == undesiredLabel) compls of
199+
Just c -> assertFailure $
200+
"Did not expect a completion with label=" <> T.unpack undesiredLabel
201+
<> ", got completion: "<> show c
202+
Nothing -> pure ()
203+
204+
disableGhcideCompletions :: Value
205+
disableGhcideCompletions = object [ "haskell" .= object ["plugin" .= object [ "ghcide-completions" .= object ["globalOn" .= False]]] ]
206+
153207
goldenWithPragmas :: TestName -> FilePath -> (TextDocumentIdentifier -> Session ()) -> TestTree
154208
goldenWithPragmas title path = goldenWithHaskellDoc pragmasPlugin title testDataDir path "expected" "hs"
155209

0 commit comments

Comments
 (0)