Skip to content

Commit 7882822

Browse files
akshaymankarfendormergify[bot]
authored
hls-pragmas-plugin: Reduce noisy completions (#3647)
* hls-pragmas-plugin: Reduce noisy completions * hls-pragmas-plugin: Simply completion and add comments * Fix typo in hls-pragma-plugin comment --------- Co-authored-by: fendor <fendor@users.noreply.github.com> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
1 parent 139dcf5 commit 7882822

File tree

3 files changed

+92
-22
lines changed

3 files changed

+92
-22
lines changed

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

Lines changed: 1 addition & 0 deletions
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

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ module Ide.Plugin.Pragmas
1414
, suggestDisableWarningDescriptor
1515
-- For testing
1616
, validPragmas
17+
, AppearWhere(..)
1718
) where
1819

1920
import Control.Lens hiding (List)
@@ -200,23 +201,41 @@ completion _ide _ complParams = do
200201
contents <- LSP.getVirtualFile $ toNormalizedUri uri
201202
fmap (Right . J.InL) $ case (contents, uriToFilePath' uri) of
202203
(Just cnts, Just _path) ->
203-
result <$> VFS.getCompletionPrefix position cnts
204+
J.List . result <$> VFS.getCompletionPrefix position cnts
204205
where
205206
result (Just pfix)
206207
| "{-# language" `T.isPrefixOf` line
207-
= J.List $ map buildCompletion
208+
= map buildCompletion
208209
(Fuzzy.simpleFilter (VFS.prefixText pfix) allPragmas)
209210
| "{-# options_ghc" `T.isPrefixOf` line
210-
= J.List $ map buildCompletion
211+
= map buildCompletion
211212
(Fuzzy.simpleFilter (VFS.prefixText pfix) flags)
212213
| "{-#" `T.isPrefixOf` line
213-
= J.List $ [ mkPragmaCompl (a <> suffix) b c
214-
| (a, b, c, w) <- validPragmas, w == NewLine ]
214+
= [ mkPragmaCompl (a <> suffix) b c
215+
| (a, b, c, w) <- validPragmas, w == NewLine
216+
]
217+
| -- Do not suggest any pragmas any of these conditions:
218+
-- 1. Current line is a an import
219+
-- 2. There is a module name right before the current word.
220+
-- Something like `Text.la` shouldn't suggest adding the
221+
-- 'LANGUAGE' pragma.
222+
-- 3. The user has not typed anything yet.
223+
"import" `T.isPrefixOf` line || not (T.null module_) || T.null word
224+
= []
215225
| otherwise
216-
= J.List $ [ mkPragmaCompl (prefix <> a <> suffix) b c
217-
| (a, b, c, _) <- validPragmas, Fuzzy.test word b]
226+
= [ mkPragmaCompl (prefix <> pragmaTemplate <> suffix) matcher detail
227+
| (pragmaTemplate, matcher, detail, appearWhere) <- validPragmas
228+
, -- Only suggest a pragma that needs its own line if the whole line
229+
-- fuzzily matches the pragma
230+
(appearWhere == NewLine && Fuzzy.test line matcher ) ||
231+
-- Only suggest a pragma that appears in the middle of a line when
232+
-- the current word is not the only thing in the line and the
233+
-- current word fuzzily matches the pragma
234+
(appearWhere == CanInline && line /= word && Fuzzy.test word matcher)
235+
]
218236
where
219237
line = T.toLower $ VFS.fullLine pfix
238+
module_ = VFS.prefixModule pfix
220239
word = VFS.prefixText pfix
221240
-- Not completely correct, may fail if more than one "{-#" exist
222241
-- , we can ignore it since it rarely happen.
@@ -230,9 +249,8 @@ completion _ide _ complParams = do
230249
| "-}" `T.isSuffixOf` line = " #"
231250
| "}" `T.isSuffixOf` line = " #-"
232251
| otherwise = " #-}"
233-
result Nothing = J.List []
252+
result Nothing = []
234253
_ -> return $ J.List []
235-
236254
-----------------------------------------------------------------------
237255

238256
-- | Pragma where exist
@@ -287,6 +305,3 @@ buildCompletion label =
287305
J.CompletionItem label (Just J.CiKeyword) Nothing Nothing
288306
Nothing Nothing Nothing Nothing Nothing Nothing Nothing
289307
Nothing Nothing Nothing Nothing Nothing Nothing
290-
291-
292-

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

Lines changed: 64 additions & 10 deletions
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
@@ -31,6 +33,7 @@ tests =
3133
, codeActionTests'
3234
, completionTests
3335
, completionSnippetTests
36+
, dontSuggestCompletionTests
3437
]
3538

3639
codeActionTests :: TestTree
@@ -139,29 +142,80 @@ completionSnippetTests :: TestTree
139142
completionSnippetTests =
140143
testGroup "expand snippet to pragma" $
141144
validPragmas <&>
142-
(\(insertText, label, detail, _) ->
143-
let input = T.toLower $ T.init label
145+
(\(insertText, label, detail, appearWhere) ->
146+
let inputPrefix =
147+
case appearWhere of
148+
NewLine -> ""
149+
CanInline -> "something "
150+
input = inputPrefix <> (T.toLower $ T.init label)
144151
in completionTest (T.unpack label)
145152
"Completion.hs" input label (Just Snippet)
146153
(Just $ "{-# " <> insertText <> " #-}") (Just detail)
147154
[0, 0, 0, 34, 0, fromIntegral $ T.length input])
148155

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

195+
provideNoCompletionsTest :: String -> FilePath -> Maybe TextEdit -> Position -> TestTree
196+
provideNoCompletionsTest testComment fileName mTextEdit pos =
197+
provideNoUndesiredCompletionsTest testComment fileName Nothing mTextEdit pos
198+
199+
provideNoUndesiredCompletionsTest :: String -> FilePath -> Maybe T.Text -> Maybe TextEdit -> Position -> TestTree
200+
provideNoUndesiredCompletionsTest testComment fileName mUndesiredLabel mTextEdit pos =
201+
testCase testComment $ runSessionWithServer pragmasCompletionPlugin testDataDir $ do
202+
doc <- openDoc fileName "haskell"
203+
_ <- waitForDiagnostics
204+
_ <- sendConfigurationChanged disableGhcideCompletions
205+
mapM_ (applyEdit doc) mTextEdit
206+
compls <- getCompletions doc pos
207+
liftIO $ case mUndesiredLabel of
208+
Nothing -> compls @?= []
209+
Just undesiredLabel -> do
210+
case find (\c -> c ^. L.label == undesiredLabel) compls of
211+
Just c -> assertFailure $
212+
"Did not expect a completion with label=" <> T.unpack undesiredLabel
213+
<> ", got completion: "<> show c
214+
Nothing -> pure ()
215+
216+
disableGhcideCompletions :: Value
217+
disableGhcideCompletions = object [ "haskell" .= object ["plugin" .= object [ "ghcide-completions" .= object ["globalOn" .= False]]] ]
218+
165219
goldenWithPragmas :: PluginTestDescriptor () -> TestName -> FilePath -> (TextDocumentIdentifier -> Session ()) -> TestTree
166220
goldenWithPragmas descriptor title path = goldenWithHaskellDoc descriptor title testDataDir path "expected" "hs"
167221

0 commit comments

Comments
 (0)