@@ -1,6 +1,7 @@
11-- Copyright (c) 2019 The DAML Authors. All rights reserved.
22-- SPDX-License-Identifier: Apache-2.0
33
4+{-# LANGUAGE AllowAmbiguousTypes #-}
45{-# LANGUAGE DuplicateRecordFields #-}
56{-# LANGUAGE PatternSynonyms #-}
67{-# LANGUAGE CPP #-}
@@ -9,30 +10,32 @@
910module Main (main) where
1011
1112import Control.Applicative.Combinators
12−import Control.Exception (catch)
13+import Control.Exception (bracket, catch)
14+import qualified Control.Lens as Lens
1315import Control.Monad
1416import Control.Monad.IO.Class (liftIO)
15−import Data.Aeson (Value)
16−import Data.Char (toLower)
17+import Data.Aeson (FromJSON, Value)
1718import Data.Foldable
18−import Data.List
19+import Data.List.Extra
1920import Data.Rope.UTF16 (Rope)
2021import qualified Data.Rope.UTF16 as Rope
2122import Development.IDE.Core.PositionMapping (fromCurrent, toCurrent)
2223import Development.IDE.GHC.Util
2324import qualified Data.Text as T
25+import Data.Typeable
2426import Development.IDE.Spans.Common
2527import Development.IDE.Test
2628import Development.IDE.Test.Runfiles
2729import Development.IDE.Types.Location
28−import qualified Language.Haskell.LSP.Test as LSPTest
29−import Language.Haskell.LSP.Test hiding (openDoc')
30+import Development.Shake (getDirectoryFilesIO)
31+import Language.Haskell.LSP.Test
3032import Language.Haskell.LSP.Messages
3133import Language.Haskell.LSP.Types
3234import Language.Haskell.LSP.Types.Capabilities
35+import qualified Language.Haskell.LSP.Types.Lens as Lsp (diagnostics, params, message)
3336import Language.Haskell.LSP.VFS (applyChange)
3437import Network.URI
35−import System.Environment.Blank (setEnv)
38+import System.Environment.Blank (getEnv, setEnv, unsetEnv)
3639import System.FilePath
3740import System.IO.Extra
3841import System.Directory
@@ -46,31 +49,35 @@ import Test.Tasty.QuickCheck
4649import Data.Maybe
4750
4851main :: IO ()
49−main = defaultMainWithRerun $ testGroup "HIE"
50− [ testSession "open close" $ do
51− doc <- openDoc' "Testing.hs" "haskell" ""
52− void (skipManyTill anyMessage message :: Session WorkDoneProgressCreateRequest)
53− void (skipManyTill anyMessage message :: Session WorkDoneProgressBeginNotification)
54− closeDoc doc
55− void (skipManyTill anyMessage message :: Session WorkDoneProgressEndNotification)
56− , initializeResponseTests
57− , completionTests
58− , cppTests
59− , diagnosticTests
60− , codeActionTests
61− , codeLensesTests
62− , outlineTests
63− , findDefinitionAndHoverTests
64− , pluginTests
65− , preprocessorTests
66− , thTests
67− , safeTests
68− , unitTests
69− , haddockTests
70− , positionMappingTests
71− , watchedFilesTests
72− , cradleTests
73− ]
52+main = do
53+ -- We mess with env vars so run single-threaded.
54+ setEnv "TASTY_NUM_THREADS" "1" True
55+ defaultMainWithRerun $ testGroup "HIE"
56+ [ testSession "open close" $ do
57+ doc <- createDoc "Testing.hs" "haskell" ""
58+ void (skipManyTill anyMessage message :: Session WorkDoneProgressCreateRequest)
59+ void (skipManyTill anyMessage message :: Session WorkDoneProgressBeginNotification)
60+ closeDoc doc
61+ void (skipManyTill anyMessage message :: Session WorkDoneProgressEndNotification)
62+ , initializeResponseTests
63+ , completionTests
64+ , cppTests
65+ , diagnosticTests
66+ , codeActionTests
67+ , codeLensesTests
68+ , outlineTests
69+ , findDefinitionAndHoverTests
70+ , pluginTests
71+ , preprocessorTests
72+ , thTests
73+ , safeTests
74+ , unitTests
75+ , haddockTests
76+ , positionMappingTests
77+ , watchedFilesTests
78+ , cradleTests
79+ , dependentFileTest
80+ ]
7481
7582initializeResponseTests :: TestTree
7683initializeResponseTests = withResource acquire release tests where
@@ -105,7 +112,7 @@ initializeResponseTests = withResource acquire release tests where
105112 , chk "NO doc link" _documentLinkProvider Nothing
106113 , chk "NO color" _colorProvider (Just $ ColorOptionsStatic False)
107114 , chk "NO folding range" _foldingRangeProvider (Just $ FoldingRangeOptionsStatic False)
108− , chk " execute command" _executeCommandProvider (Just $ ExecuteCommandOptions $ List ["typesignature.add"])
115+ , che " execute command" _executeCommandProvider (Just $ ExecuteCommandOptions $ List ["typesignature.add"])
109116 , chk " workspace" _workspace (Just $ WorkspaceOptions (Just WorkspaceFolderOptions{_supported = Just True, _changeNotifications = Just ( WorkspaceFolderChangeNotificationsBool True )}))
110117 , chk "NO experimental" _experimental Nothing
111118 ] where
@@ -121,8 +128,17 @@ initializeResponseTests = withResource acquire release tests where
121128 chk title getActual expected =
122129 testCase title $ getInitializeResponse >>= \ir -> expected @=? (getActual . innerCaps) ir
123130
131+ che :: TestName -> (InitializeResponseCapabilitiesInner -> Maybe ExecuteCommandOptions) -> Maybe ExecuteCommandOptions -> TestTree
132+ che title getActual _expected = testCase title doTest
133+ where
134+ doTest = do
135+ ir <- getInitializeResponse
136+ let Just ExecuteCommandOptions {_commands = List [command]} = getActual $ innerCaps ir
137+ True @=? T.isSuffixOf "typesignature.add" command
138+
139+
124140 innerCaps :: InitializeResponse -> InitializeResponseCapabilitiesInner
125− innerCaps (ResponseMessage _ _ (Just (InitializeResponseCapabilities c)) _) = c
141+ innerCaps (ResponseMessage _ _ (Right (InitializeResponseCapabilities c))) = c
126142 innerCaps _ = error "this test only expects inner capabilities"
127143
128144 acquire :: IO InitializeResponse
@@ -136,7 +152,7 @@ diagnosticTests :: TestTree
136152diagnosticTests = testGroup "diagnostics"
137153 [ testSessionWait "fix syntax error" $ do
138154 let content = T.unlines [ "module Testing wher" ]
139− doc <- openDoc' "Testing.hs" "haskell" content
155+ doc <- createDoc "Testing.hs" "haskell" content
140156 expectDiagnostics [("Testing.hs", [(DsError, (0, 15), "parse error")])]
141157 let change = TextDocumentContentChangeEvent
142158 { _range = Just (Range (Position 0 15) (Position 0 19))
@@ -147,7 +163,7 @@ diagnosticTests = testGroup "diagnostics"
147163 expectDiagnostics [("Testing.hs", [])]
148164 , testSessionWait "introduce syntax error" $ do
149165 let content = T.unlines [ "module Testing where" ]
150− doc <- openDoc' "Testing.hs" "haskell" content
166+ doc <- createDoc "Testing.hs" "haskell" content
151167 void $ skipManyTill anyMessage (message :: Session WorkDoneProgressCreateRequest)
152168 void $ skipManyTill anyMessage (message :: Session WorkDoneProgressBeginNotification)
153169 let change = TextDocumentContentChangeEvent
@@ -165,7 +181,7 @@ diagnosticTests = testGroup "diagnostics"
165181 , "bar :: Int -> Int -> Int"
166182 , "bar a b = cd + b"
167183 ]
168− _ <- openDoc' "Testing.hs" "haskell" content
184+ _ <- createDoc "Testing.hs" "haskell" content
169185 expectDiagnostics
170186 [ ( "Testing.hs"
171187 , [ (DsError, (2, 14), "Variable not in scope: ab")
@@ -179,7 +195,7 @@ diagnosticTests = testGroup "diagnostics"
179195 , "foo :: Int -> String -> Int"
180196 , "foo a b = a + b"
181197 ]
182− _ <- openDoc' "Testing.hs" "haskell" content
198+ _ <- createDoc "Testing.hs" "haskell" content
183199 expectDiagnostics
184200 [ ( "Testing.hs"
185201 , [(DsError, (2, 14), "Couldn't match type '[Char]' with 'Int'")]
@@ -191,7 +207,7 @@ diagnosticTests = testGroup "diagnostics"
191207 , "foo :: Int -> String"
192208 , "foo a = _ a"
193209 ]
194− _ <- openDoc' "Testing.hs" "haskell" content
210+ _ <- createDoc "Testing.hs" "haskell" content
195211 expectDiagnostics
196212 [ ( "Testing.hs"
197213 , [(DsError, (2, 8), "Found hole: _ :: Int -> String")]
@@ -213,24 +229,23 @@ diagnosticTests = testGroup "diagnostics"
213229 [ ("A.hs", [(DsError, (2,4), aMessage)])
214230 , ("B.hs", [(DsError, (3,4), bMessage)])]
215231 deferralTest title binding msg = testSessionWait title $ do
216− _ <- openDoc' "A.hs" "haskell" $ sourceA binding
217− _ <- openDoc' "B.hs" "haskell" sourceB
232+ _ <- createDoc "A.hs" "haskell" $ sourceA binding
233+ _ <- createDoc "B.hs" "haskell" sourceB
218234 expectDiagnostics $ expectedDs msg
219235 in
220236 [ deferralTest "type error" "True" "Couldn't match expected type"
221237 , deferralTest "typed hole" "_" "Found hole"
222238 , deferralTest "out of scope var" "unbound" "Variable not in scope"
223− , deferralTest "message shows error" "True" "A.hs:3:5: error:"
224239 ]
225240
226241 , testSessionWait "remove required module" $ do
227242 let contentA = T.unlines [ "module ModuleA where" ]
228− docA <- openDoc' "ModuleA.hs" "haskell" contentA
243+ docA <- createDoc "ModuleA.hs" "haskell" contentA
229244 let contentB = T.unlines
230245 [ "module ModuleB where"
231246 , "import ModuleA"
232247 ]
233− _ <- openDoc' "ModuleB.hs" "haskell" contentB
248+ _ <- createDoc "ModuleB.hs" "haskell" contentB
234249 let change = TextDocumentContentChangeEvent
235250 { _range = Just (Range (Position 0 0) (Position 0 20))
236251 , _rangeLength = Nothing
@@ -243,20 +258,20 @@ diagnosticTests = testGroup "diagnostics"
243258 [ "module ModuleB where"
244259 , "import ModuleA"
245260 ]
246− _ <- openDoc' "ModuleB.hs" "haskell" contentB
261+ _ <- createDoc "ModuleB.hs" "haskell" contentB
247262 expectDiagnostics [("ModuleB.hs", [(DsError, (1, 7), "Could not find module")])]
248263 let contentA = T.unlines [ "module ModuleA where" ]
249− _ <- openDoc' "ModuleA.hs" "haskell" contentA
264+ _ <- createDoc "ModuleA.hs" "haskell" contentA
250265 expectDiagnostics [("ModuleB.hs", [])]
251266 , testSessionWait "add missing module (non workspace)" $ do
252267 let contentB = T.unlines
253268 [ "module ModuleB where"
254269 , "import ModuleA"
255270 ]
256− _ <- openDoc'' "/tmp/ModuleB.hs" "haskell" contentB
271+ _ <- createDoc "/tmp/ModuleB.hs" "haskell" contentB
257272 expectDiagnostics [("/tmp/ModuleB.hs", [(DsError, (1, 7), "Could not find module")])]
258273 let contentA = T.unlines [ "module ModuleA where" ]
259− _ <- openDoc'' "/tmp/ModuleA.hs" "haskell" contentA
274+ _ <- createDoc "/tmp/ModuleA.hs" "haskell" contentA
260275 expectDiagnostics [("/tmp/ModuleB.hs", [])]
261276 , testSessionWait "cyclic module dependency" $ do
262277 let contentA = T.unlines
@@ -267,8 +282,8 @@ diagnosticTests = testGroup "diagnostics"
267282 [ "module ModuleB where"
268283 , "import ModuleA"
269284 ]
270− _ <- openDoc' "ModuleA.hs" "haskell" contentA
271− _ <- openDoc' "ModuleB.hs" "haskell" contentB
285+ _ <- createDoc "ModuleA.hs" "haskell" contentA
286+ _ <- createDoc "ModuleB.hs" "haskell" contentB
272287 expectDiagnostics
273288 [ ( "ModuleA.hs"
274289 , [(DsError, (1, 7), "Cyclic module dependency between ModuleA, ModuleB")]
@@ -289,9 +304,9 @@ diagnosticTests = testGroup "diagnostics"
289304 let contentBboot = T.unlines
290305 [ "module ModuleB where"
291306 ]
292− _ <- openDoc' "ModuleA.hs" "haskell" contentA
293− _ <- openDoc' "ModuleB.hs" "haskell" contentB
294− _ <- openDoc' "ModuleB.hs-boot" "haskell" contentBboot
307+ _ <- createDoc "ModuleA.hs" "haskell" contentA
308+ _ <- createDoc "ModuleB.hs" "haskell" contentB
309+ _ <- createDoc "ModuleB.hs-boot" "haskell" contentBboot
295310 expectDiagnostics []
296311 , testSessionWait "correct reference used with hs-boot" $ do
297312 let contentB = T.unlines
@@ -313,10 +328,10 @@ diagnosticTests = testGroup "diagnostics"
313328 -- resolved to the hs-boot file
314329 , "y = x"
315330 ]
316− _ <- openDoc' "ModuleB.hs" "haskell" contentB
317− _ <- openDoc' "ModuleA.hs" "haskell" contentA
318− _ <- openDoc' "ModuleA.hs-boot" "haskell" contentAboot
319− _ <- openDoc' "ModuleC.hs" "haskell" contentC
331+ _ <- createDoc "ModuleB.hs" "haskell" contentB
332+ _ <- createDoc "ModuleA.hs" "haskell" contentA
333+ _ <- createDoc "ModuleA.hs-boot" "haskell" contentAboot
334+ _ <- createDoc "ModuleC.hs" "haskell" contentC
320335 expectDiagnostics []
321336 , testSessionWait "redundant import" $ do
322337 let contentA = T.unlines ["module ModuleA where"]
@@ -325,8 +340,8 @@ diagnosticTests = testGroup "diagnostics"
325340 , "module ModuleB where"
326341 , "import ModuleA"
327342 ]
328− _ <- openDoc' "ModuleA.hs" "haskell" contentA
329− _ <- openDoc' "ModuleB.hs" "haskell" contentB
343+ _ <- createDoc "ModuleA.hs" "haskell" contentA
344+ _ <- createDoc "ModuleB.hs" "haskell" contentB
330345 expectDiagnostics
331346 [ ( "ModuleB.hs"
332347 , [(DsWarning, (2, 0), "The import of 'ModuleA' is redundant")]
@@ -348,8 +363,8 @@ diagnosticTests = testGroup "diagnostics"
348363 , "wrong1 = ThisList.map"
349364 , "wrong2 = BaseList.x"
350365 ]
351− _ <- openDoc' "Data/List.hs" "haskell" thisDataListContent
352− _ <- openDoc' "Main.hs" "haskell" mainContent
366+ _ <- createDoc "Data/List.hs" "haskell" thisDataListContent
367+ _ <- createDoc "Main.hs" "haskell" mainContent
353368 expectDiagnostics
354369 [ ( "Main.hs"
355370 , [(DsError, (6, 9), "Not in scope: \8216ThisList.map\8217")
@@ -364,7 +379,7 @@ diagnosticTests = testGroup "diagnostics"
364379 , "foo :: Ord a => a -> Int"
365380 , "foo a = 1"
366381 ]
367− _ <- openDoc' "Foo.hs" "haskell" fooContent
382+ _ <- createDoc "Foo.hs" "haskell" fooContent
368383 expectDiagnostics
369384 [ ( "Foo.hs"
370385 -- The test is to make sure that warnings contain unqualified names
@@ -389,18 +404,18 @@ diagnosticTests = testGroup "diagnostics"
389404 Just pathB <- pure $ uriToFilePath uriB
390405 uriB <- pure $
391406 let (drive, suffix) = splitDrive pathB
392− in filePathToUri (joinDrive (map toLower drive ) suffix)
407+ in filePathToUri (joinDrive (lower drive) suffix)
393408 liftIO $ createDirectoryIfMissing True (takeDirectory pathB)
394409 liftIO $ writeFileUTF8 pathB $ T.unpack bContent
395410 uriA <- getDocUri "A/A.hs"
396411 Just pathA <- pure $ uriToFilePath uriA
397412 uriA <- pure $
398413 let (drive, suffix) = splitDrive pathA
399− in filePathToUri (joinDrive (map toLower drive ) suffix)
414+ in filePathToUri (joinDrive (lower drive) suffix)
400415 let itemA = TextDocumentItem uriA "haskell" 0 aContent
401416 let a = TextDocumentIdentifier uriA
402417 sendNotification TextDocumentDidOpen (DidOpenTextDocumentParams itemA)
403− diagsNot <- skipManyTill anyMessage message :: Session PublishDiagnosticsNotification
418+ diagsNot <- skipManyTill anyMessage diagnostic
404419 let PublishDiagnosticsParams fileUri diags = _params (diagsNot :: PublishDiagnosticsNotification)
405420 -- Check that if we put a lower-case drive in for A.A
406421 -- the diagnostics for A.B will also be lower-case.
@@ -409,6 +424,38 @@ diagnosticTests = testGroup "diagnostics"
409424 liftIO $ unless ("redundant" `T.isInfixOf` msg) $
410425 assertFailure ("Expected redundant import but got " <> T.unpack msg)
411426 closeDoc a
427+ , testSessionWait "haddock parse error" $ do
428+ let fooContent = T.unlines
429+ [ "module Foo where"
430+ , "foo :: Int"
431+ , "foo = 1 {-|-}"
432+ ]
433+ _ <- createDoc "Foo.hs" "haskell" fooContent
434+ expectDiagnostics
435+ [ ( "Foo.hs"
436+ , [(DsError, (2, 8), "Parse error on input")
437+ ]
438+ )
439+ ]
440+ , testSessionWait "strip file path" $ do
441+ let
442+ name = "Testing"
443+ content = T.unlines
444+ [ "module " <> name <> " where"
445+ , "value :: Maybe ()"
446+ , "value = [()]"
447+ ]
448+ _ <- createDoc (T.unpack name <> ".hs") "haskell" content
449+ notification <- skipManyTill anyMessage diagnostic
450+ let
451+ offenders =
452+ Lsp.params .
453+ Lsp.diagnostics .
454+ Lens.folded .
455+ Lsp.message .
456+ Lens.filtered (T.isInfixOf ("/" <> name <> ".hs:"))
457+ failure msg = liftIO $ assertFailure $ "Expected file path to be stripped but got " <> T.unpack msg
458+ Lens.mapMOf_ offenders failure notification
412459 ]
413460
414461codeActionTests :: TestTree
@@ -434,26 +481,26 @@ codeLensesTests = testGroup "code lenses"
434481watchedFilesTests :: TestTree
435482watchedFilesTests = testGroup "watched files"
436483 [ testSession' "workspace files" $ \sessionDir -> do
437− liftIO $ writeFile (sessionDir </> "hie.yaml") $ "cradle: {direct: {arguments: [\"-isrc\"]}}"
438− _ <- openDoc' "A.hs" "haskell" "{-#LANGUAGE NoImplicitPrelude #-}\nmodule A where\nimport B"
439− watchedFileRegs <- getWatchedFilesSubscriptionsUntilProgressEnd
440−
441− -- Expect 6 subscriptions (A does not get any because it's VFS):
442− -- - /path-to-workspace/B.hs
443− -- - /path-to-workspace/B.lhs
444− -- - B.hs
445− -- - B.lhs
446− -- - src/B.hs
447− -- - src/B.lhs
448− liftIO $ length watchedFileRegs @?= 6
484+ liftIO $ writeFile (sessionDir </> "hie.yaml") "cradle: {direct: {arguments: [\"-isrc\"]}}"
485+ _doc <- createDoc "A.hs" "haskell" "{-#LANGUAGE NoImplicitPrelude #-}\nmodule A where\nimport WatchedFilesMissingModule"
486+ watchedFileRegs <- getWatchedFilesSubscriptionsUntil @PublishDiagnosticsNotification
487+
488+ -- Expect 4 subscriptions (A does not get any because it's VFS):
489+ -- - /path-to-workspace/WatchedFilesMissingModule.hs
490+ -- - /path-to-workspace/WatchedFilesMissingModule.lhs
491+ -- - /path-to-workspace/src/WatchedFilesMissingModule.hs
492+ -- - /path-to-workspace/src/WatchedFilesMissingModule.lhs
493+ liftIO $ length watchedFileRegs @?= 4
449494
450495 , testSession' "non workspace file" $ \sessionDir -> do
451− liftIO $ writeFile (sessionDir </> "hie.yaml") $ "cradle: {direct: {arguments: [\"-i/tmp\"]}}"
452− _ <- openDoc' "A.hs" "haskell" "{-# LANGUAGE NoImplicitPrelude#-}\nmodule A where\nimport B"
453− watchedFileRegs <- getWatchedFilesSubscriptionsUntilProgressEnd
496+ liftIO $ writeFile (sessionDir </> "hie.yaml") "cradle: {direct: {arguments: [\"-i/tmp\"]}}"
497+ _doc <- createDoc "A.hs" "haskell" "{-# LANGUAGE NoImplicitPrelude#-}\nmodule A where\nimport WatchedFilesMissingModule"
498+ watchedFileRegs <- getWatchedFilesSubscriptionsUntil @PublishDiagnosticsNotification
454499
455− -- Expect 4 subscriptions:
456− liftIO $ length watchedFileRegs @?= 4
500+ -- Expect 2 subscriptions (/tmp does not get any as it is out of the workspace):
501+ -- - /path-to-workspace/WatchedFilesMissingModule.hs
502+ -- - /path-to-workspace/WatchedFilesMissingModule.lhs
503+ liftIO $ length watchedFileRegs @?= 2
457504
458505 -- TODO add a test for didChangeWorkspaceFolder
459506 ]
@@ -466,7 +513,7 @@ renameActionTests = testGroup "rename actions"
466513 , "foo :: Int -> Int"
467514 , "foo argName = argNme"
468515 ]
469− doc <- openDoc' "Testing.hs" "haskell" content
516+ doc <- createDoc "Testing.hs" "haskell" content
470517 _ <- waitForDiagnostics
471518 action <- findCodeAction doc (Range (Position 2 14) (Position 2 20)) "Replace with ‘argName’"
472519 executeCodeAction action
@@ -484,7 +531,7 @@ renameActionTests = testGroup "rename actions"
484531 , "foo :: Maybe a -> [a]"
485532 , "foo = maybToList"
486533 ]
487− doc <- openDoc' "Testing.hs" "haskell" content
534+ doc <- createDoc "Testing.hs" "haskell" content
488535 _ <- waitForDiagnostics
489536 action <- findCodeAction doc (Range (Position 3 6) (Position 3 16)) "Replace with ‘maybeToList’"
490537 executeCodeAction action
@@ -502,7 +549,7 @@ renameActionTests = testGroup "rename actions"
502549 , "foo :: Char -> Char -> Char -> Char"
503550 , "foo argument1 argument2 argument3 = argumentX"
504551 ]
505− doc <- openDoc' "Testing.hs" "haskell" content
552+ doc <- createDoc "Testing.hs" "haskell" content
506553 _ <- waitForDiagnostics
507554 _ <- findCodeActions doc (Range (Position 2 36) (Position 2 45))
508555 ["Replace with ‘argument1’", "Replace with ‘argument2’", "Replace with ‘argument3’"]
@@ -514,7 +561,7 @@ renameActionTests = testGroup "rename actions"
514561 , "monus x y = max 0 (x - y)"
515562 , "foo x y = x `monnus` y"
516563 ]
517− doc <- openDoc' "Testing.hs" "haskell" content
564+ doc <- createDoc "Testing.hs" "haskell" content
518565 _ <- waitForDiagnostics
519566 actionsOrCommands <- getCodeActions doc (Range (Position 3 12) (Position 3 20))
520567 [fixTypo] <- pure [action | CACodeAction action@CodeAction{ _title = actionTitle } <- actionsOrCommands, "monus" `T.isInfixOf` actionTitle ]
@@ -537,7 +584,7 @@ typeWildCardActionTests = testGroup "type wildcard actions"
537584 , "func :: _"
538585 , "func x = x"
539586 ]
540− doc <- openDoc' "Testing.hs" "haskell" content
587+ doc <- createDoc "Testing.hs" "haskell" content
541588 _ <- waitForDiagnostics
542589 actionsOrCommands <- getCodeActions doc (Range (Position 2 1) (Position 2 10))
543590 let [addSignature] = [action | CACodeAction action@CodeAction { _title = actionTitle } <- actionsOrCommands
@@ -557,7 +604,7 @@ typeWildCardActionTests = testGroup "type wildcard actions"
557604 , "func :: _"
558605 , "func x y = x + y"
559606 ]
560− doc <- openDoc' "Testing.hs" "haskell" content
607+ doc <- createDoc "Testing.hs" "haskell" content
561608 _ <- waitForDiagnostics
562609 actionsOrCommands <- getCodeActions doc (Range (Position 2 1) (Position 2 10))
563610 let [addSignature] = [action | CACodeAction action@CodeAction { _title = actionTitle } <- actionsOrCommands
@@ -580,7 +627,7 @@ typeWildCardActionTests = testGroup "type wildcard actions"
580627 , " y = x * 2"
581628 , " in y"
582629 ]
583− doc <- openDoc' "Testing.hs" "haskell" content
630+ doc <- createDoc "Testing.hs" "haskell" content
584631 _ <- waitForDiagnostics
585632 actionsOrCommands <- getCodeActions doc (Range (Position 4 1) (Position 4 10))
586633 let [addSignature] = [action | CACodeAction action@CodeAction { _title = actionTitle } <- actionsOrCommands
@@ -605,7 +652,7 @@ removeImportTests = testGroup "remove import actions"
605652 let contentA = T.unlines
606653 [ "module ModuleA where"
607654 ]
608− _docA <- openDoc' "ModuleA.hs" "haskell" contentA
655+ _docA <- createDoc "ModuleA.hs" "haskell" contentA
609656 let contentB = T.unlines
610657 [ "{-# OPTIONS_GHC -Wunused-imports #-}"
611658 , "module ModuleB where"
@@ -613,7 +660,7 @@ removeImportTests = testGroup "remove import actions"
613660 , "stuffB :: Integer"
614661 , "stuffB = 123"
615662 ]
616− docB <- openDoc' "ModuleB.hs" "haskell" contentB
663+ docB <- createDoc "ModuleB.hs" "haskell" contentB
617664 _ <- waitForDiagnostics
618665 [CACodeAction action@CodeAction { _title = actionTitle }]
619666 <- getCodeActions docB (Range (Position 2 0) (Position 2 5))
@@ -631,7 +678,7 @@ removeImportTests = testGroup "remove import actions"
631678 let contentA = T.unlines
632679 [ "module ModuleA where"
633680 ]
634− _docA <- openDoc' "ModuleA.hs" "haskell" contentA
681+ _docA <- createDoc "ModuleA.hs" "haskell" contentA
635682 let contentB = T.unlines
636683 [ "{-# OPTIONS_GHC -Wunused-imports #-}"
637684 , "module ModuleB where"
@@ -639,7 +686,7 @@ removeImportTests = testGroup "remove import actions"
639686 , "stuffB :: Integer"
640687 , "stuffB = 123"
641688 ]
642− docB <- openDoc' "ModuleB.hs" "haskell" contentB
689+ docB <- createDoc "ModuleB.hs" "haskell" contentB
643690 _ <- waitForDiagnostics
644691 [CACodeAction action@CodeAction { _title = actionTitle }]
645692 <- getCodeActions docB (Range (Position 2 0) (Position 2 5))
@@ -661,14 +708,14 @@ removeImportTests = testGroup "remove import actions"
661708 , "stuffB = 123"
662709 , "stuffC = ()"
663710 ]
664− _docA <- openDoc' "ModuleA.hs" "haskell" contentA
711+ _docA <- createDoc "ModuleA.hs" "haskell" contentA
665712 let contentB = T.unlines
666713 [ "{-# OPTIONS_GHC -Wunused-imports #-}"
667714 , "module ModuleB where"
668715 , "import ModuleA (stuffA, stuffB, stuffC, stuffA)"
669716 , "main = print stuffB"
670717 ]
671− docB <- openDoc' "ModuleB.hs" "haskell" contentB
718+ docB <- createDoc "ModuleB.hs" "haskell" contentB
672719 _ <- waitForDiagnostics
673720 [CACodeAction action@CodeAction { _title = actionTitle }]
674721 <- getCodeActions docB (Range (Position 2 0) (Position 2 5))
@@ -690,22 +737,18 @@ removeImportTests = testGroup "remove import actions"
690737 , "stuffB :: Integer"
691738 , "stuffB = 123"
692739 ]
693− _docA <- openDoc' "ModuleA.hs" "haskell" contentA
740+ _docA <- createDoc "ModuleA.hs" "haskell" contentA
694741 let contentB = T.unlines
695742 [ "{-# OPTIONS_GHC -Wunused-imports #-}"
696743 , "module ModuleB where"
697744 , "import qualified ModuleA as A ((<?>), stuffB, (!!))"
698745 , "main = print A.stuffB"
699746 ]
700− docB <- openDoc' "ModuleB.hs" "haskell" contentB
747+ docB <- createDoc "ModuleB.hs" "haskell" contentB
701748 _ <- waitForDiagnostics
702749 [CACodeAction action@CodeAction { _title = actionTitle }]
703750 <- getCodeActions docB (Range (Position 2 0) (Position 2 5))
704−#if MIN_GHC_API_VERSION(8,6,0)
705751 liftIO $ "Remove !!, <?> from import" @=? actionTitle
706−#else
707− liftIO $ "Remove A.!!, A.<?> from import" @=? actionTitle
708−#endif
709752 executeCodeAction action
710753 contentAfterAction <- documentContents docB
711754 let expectedContentAfterAction = T.unlines
@@ -722,14 +765,14 @@ removeImportTests = testGroup "remove import actions"
722765 , "stuffB :: Integer"
723766 , "stuffB = 123"
724767 ]
725− _docA <- openDoc' "ModuleA.hs" "haskell" contentA
768+ _docA <- createDoc "ModuleA.hs" "haskell" contentA
726769 let contentB = T.unlines
727770 [ "{-# OPTIONS_GHC -Wunused-imports #-}"
728771 , "module ModuleB where"
729772 , "import ModuleA (A(..), stuffB)"
730773 , "main = print stuffB"
731774 ]
732− docB <- openDoc' "ModuleB.hs" "haskell" contentB
775+ docB <- createDoc "ModuleB.hs" "haskell" contentB
733776 _ <- waitForDiagnostics
734777 [CACodeAction action@CodeAction { _title = actionTitle }]
735778 <- getCodeActions docB (Range (Position 2 0) (Position 2 5))
@@ -749,14 +792,14 @@ removeImportTests = testGroup "remove import actions"
749792 , "data D = A | B"
750793 , "data E = F"
751794 ]
752− _docA <- openDoc' "ModuleA.hs" "haskell" contentA
795+ _docA <- createDoc "ModuleA.hs" "haskell" contentA
753796 let contentB = T.unlines
754797 [ "{-# OPTIONS_GHC -Wunused-imports #-}"
755798 , "module ModuleB where"
756799 , "import ModuleA (D(A,B), E(F))"
757800 , "main = B"
758801 ]
759− docB <- openDoc' "ModuleB.hs" "haskell" contentB
802+ docB <- createDoc "ModuleB.hs" "haskell" contentB
760803 _ <- waitForDiagnostics
761804 [CACodeAction action@CodeAction { _title = actionTitle }]
762805 <- getCodeActions docB (Range (Position 2 0) (Position 2 5))
@@ -897,8 +940,8 @@ extendImportTests = testGroup "extend import actions"
897940 ]
898941 where
899942 template contentA contentB range expectedAction expectedContentB = do
900− _docA <- openDoc' "ModuleA.hs" "haskell" contentA
901− docB <- openDoc' "ModuleB.hs" "haskell" contentB
943+ _docA <- createDoc "ModuleA.hs" "haskell" contentA
944+ docB <- createDoc "ModuleB.hs" "haskell" contentB
902945 _ <- waitForDiagnostics
903946 CACodeAction action@CodeAction { _title = actionTitle } : _
904947 <- sortOn (\(CACodeAction CodeAction{_title=x}) -> x) <$>
@@ -938,6 +981,8 @@ suggestImportTests = testGroup "suggest import actions"
938981 , test True [] "f :: Typeable a => a" ["f = undefined"] "import Data.Typeable (Typeable)"
939982 , test True [] "f = pack" [] "import Data.Text (pack)"
940983 , test True [] "f :: Text" ["f = undefined"] "import Data.Text (Text)"
984+ , test True [] "f = [] & id" [] "import Data.Function ((&))"
985+ , test True [] "f = (&) [] id" [] "import Data.Function ((&))"
941986 ]
942987 ]
943988 where
@@ -946,19 +991,20 @@ suggestImportTests = testGroup "suggest import actions"
946991 after = T.unlines $ "module A where" : ["import " <> x | x <- imps] ++ [newImp] ++ def : other
947992 cradle = "cradle: {direct: {arguments: [-hide-all-packages, -package, base, -package, text, -package-env, -]}}"
948993 liftIO $ writeFileUTF8 (dir </> "hie.yaml") cradle
949− doc <- openDoc' "Test.hs" "haskell" before
994+ doc <- createDoc "Test.hs" "haskell" before
950995 _diags <- waitForDiagnostics
951996 let defLine = length imps + 1
952997 range = Range (Position defLine 0) (Position defLine maxBound)
953998 actions <- getCodeActions doc range
954− case wanted of
955− False ->
956− liftIO $ [_title | CACodeAction CodeAction{_title} <- actions, _title == newImp ] @?= []
957− True -> do
958− action <- liftIO $ pickActionWithTitle newImp actions
959− executeCodeAction action
960− contentAfterAction <- documentContents doc
961− liftIO $ after @=? contentAfterAction
999+ if wanted
1000+ then do
1001+ action <- liftIO $ pickActionWithTitle newImp actions
1002+ executeCodeAction action
1003+ contentAfterAction <- documentContents doc
1004+ liftIO $ after @=? contentAfterAction
1005+ else
1006+ liftIO $ [_title | CACodeAction CodeAction{_title} <- actions, _title == newImp ] @?= []
1007+
9621008
9631009addExtensionTests :: TestTree
9641010addExtensionTests = testGroup "add language extension actions"
@@ -1005,7 +1051,7 @@ addExtensionTests = testGroup "add language extension actions"
10051051 ]
10061052 where
10071053 template initialContent range expectedAction expectedContents = do
1008− doc <- openDoc' "Module.hs" "haskell" initialContent
1054+ doc <- createDoc "Module.hs" "haskell" initialContent
10091055 _ <- waitForDiagnostics
10101056 CACodeAction action@CodeAction { _title = actionTitle } : _
10111057 <- sortOn (\(CACodeAction CodeAction{_title=x}) -> x) <$>
@@ -1028,7 +1074,7 @@ insertNewDefinitionTests = testGroup "insert new definition actions"
10281074 [""
10291075 ,"someOtherCode = ()"
10301076 ]
1031− docB <- openDoc' "ModuleB.hs" "haskell" (T.unlines $ txtB ++ txtB')
1077+ docB <- createDoc "ModuleB.hs" "haskell" (T.unlines $ txtB ++ txtB')
10321078 _ <- waitForDiagnostics
10331079 CACodeAction action@CodeAction { _title = actionTitle } : _
10341080 <- sortOn (\(CACodeAction CodeAction{_title=x}) -> x) <$>
@@ -1052,7 +1098,7 @@ insertNewDefinitionTests = testGroup "insert new definition actions"
10521098 [""
10531099 ,"someOtherCode = ()"
10541100 ]
1055− docB <- openDoc' "ModuleB.hs" "haskell" (T.unlines $ txtB ++ txtB')
1101+ docB <- createDoc "ModuleB.hs" "haskell" (T.unlines $ txtB ++ txtB')
10561102 _ <- waitForDiagnostics
10571103 CACodeAction action@CodeAction { _title = actionTitle } : _
10581104 <- sortOn (\(CACodeAction CodeAction{_title=x}) -> x) <$>
@@ -1091,8 +1137,8 @@ fixConstructorImportTests = testGroup "fix import actions"
10911137 ]
10921138 where
10931139 template contentA contentB range expectedAction expectedContentB = do
1094− _docA <- openDoc' "ModuleA.hs" "haskell" contentA
1095− docB <- openDoc' "ModuleB.hs" "haskell" contentB
1140+ _docA <- createDoc "ModuleA.hs" "haskell" contentA
1141+ docB <- createDoc "ModuleB.hs" "haskell" contentB
10961142 _diags <- waitForDiagnostics
10971143 CACodeAction action@CodeAction { _title = actionTitle } : _
10981144 <- sortOn (\(CACodeAction CodeAction{_title=x}) -> x) <$>
@@ -1111,7 +1157,7 @@ importRenameActionTests = testGroup "import rename actions"
11111157 [ "module Testing where"
11121158 , "import Data.Mape"
11131159 ]
1114− doc <- openDoc' "Testing.hs" "haskell" content
1160+ doc <- createDoc "Testing.hs" "haskell" content
11151161 _ <- waitForDiagnostics
11161162 actionsOrCommands <- getCodeActions doc (Range (Position 2 8) (Position 2 16))
11171163 let [changeToMap] = [action | CACodeAction action@CodeAction{ _title = actionTitle } <- actionsOrCommands, ("Data." <> modname) `T.isInfixOf` actionTitle ]
@@ -1148,7 +1194,7 @@ fillTypedHoleTests = let
11481194 newA newB newC = testSession (T.unpack actionTitle) $ do
11491195 let originalCode = sourceCode oldA oldB oldC
11501196 let expectedCode = sourceCode newA newB newC
1151− doc <- openDoc' "Testing.hs" "haskell" originalCode
1197+ doc <- createDoc "Testing.hs" "haskell" originalCode
11521198 _ <- waitForDiagnostics
11531199 actionsOrCommands <- getCodeActions doc (Range (Position 9 0) (Position 9 maxBound))
11541200 chosenAction <- liftIO $ pickActionWithTitle actionTitle actionsOrCommands
@@ -1196,7 +1242,7 @@ addSigActionTests = let
11961242 def >:: sig = testSession (T.unpack def) $ do
11971243 let originalCode = before def
11981244 let expectedCode = after' def sig
1199− doc <- openDoc' "Sigs.hs" "haskell" originalCode
1245+ doc <- createDoc "Sigs.hs" "haskell" originalCode
12001246 _ <- waitForDiagnostics
12011247 actionsOrCommands <- getCodeActions doc (Range (Position 3 1) (Position 3 maxBound))
12021248 chosenAction <- liftIO $ pickActionWithTitle ("add signature: " <> sig) actionsOrCommands
@@ -1228,7 +1274,7 @@ addSigLensesTests = let
12281274 sigSession withMissing def sig = testSession (T.unpack def) $ do
12291275 let originalCode = before withMissing def
12301276 let expectedCode = after' withMissing def sig
1231− doc <- openDoc' "Sigs.hs" "haskell" originalCode
1277+ doc <- createDoc "Sigs.hs" "haskell" originalCode
12321278 [CodeLens {_command = Just c}] <- getCodeLenses doc
12331279 executeCommand c
12341280 modifiedCode <- getDocumentEdit doc
@@ -1250,24 +1296,19 @@ addSigLensesTests = let
12501296 ]
12511297 ]
12521298
1253−findDefinitionAndHoverTests :: TestTree
1254−findDefinitionAndHoverTests = let
1255−
1256− tst (get, check) pos targetRange title = testSession title $ do
1257− doc <- openTestDataDoc sourceFilePath
1258− found <- get doc pos
1259− check found targetRange
1260−
1261− checkDefs :: [Location] -> [Expect] -> Session ()
1262− checkDefs defs expectations = traverse_ check expectations where
1299+checkDefs :: [Location] -> Session [Expect] -> Session ()
1300+checkDefs defs mkExpectations = traverse_ check =<< mkExpectations where
12631301
1264− check (ExpectRange expectedRange) = do
1265− assertNDefinitionsFound 1 defs
1266− assertRangeCorrect (head defs) expectedRange
1267− check ExpectNoDefinitions = do
1268− assertNDefinitionsFound 0 defs
1269− check ExpectExternFail = liftIO $ assertFailure "Expecting to fail to find in external file"
1270− check _ = pure () -- all other expectations not relevant to getDefinition
1302+ check (ExpectRange expectedRange) = do
1303+ assertNDefinitionsFound 1 defs
1304+ assertRangeCorrect (head defs) expectedRange
1305+ check (ExpectLocation expectedLocation) = do
1306+ assertNDefinitionsFound 1 defs
1307+ liftIO $ head defs @?= expectedLocation
1308+ check ExpectNoDefinitions = do
1309+ assertNDefinitionsFound 0 defs
1310+ check ExpectExternFail = liftIO $ assertFailure "Expecting to fail to find in external file"
1311+ check _ = pure () -- all other expectations not relevant to getDefinition
12711312
12721313 assertNDefinitionsFound :: Int -> [a] -> Session ()
12731314 assertNDefinitionsFound n defs = liftIO $ assertEqual "number of definitions" n (length defs)
@@ -1275,8 +1316,19 @@ findDefinitionAndHoverTests = let
12751316 assertRangeCorrect Location{_range = foundRange} expectedRange =
12761317 liftIO $ expectedRange @=? foundRange
12771318
1278− checkHover :: Maybe Hover -> [Expect] -> Session ()
1279− checkHover hover expectations = traverse_ check expectations where
1319+
1320+findDefinitionAndHoverTests :: TestTree
1321+findDefinitionAndHoverTests = let
1322+
1323+ tst (get, check) pos targetRange title = testSessionWithExtraFiles "hover" title $ \dir -> do
1324+ doc <- openTestDataDoc (dir </> sourceFilePath)
1325+ found <- get doc pos
1326+ check found targetRange
1327+
1328+
1329+
1330+ checkHover :: Maybe Hover -> Session [Expect] -> Session ()
1331+ checkHover hover expectations = traverse_ check =<< expectations where
12801332
12811333 check expected =
12821334 case hover of
@@ -1319,9 +1371,12 @@ findDefinitionAndHoverTests = let
13191371
13201372 mkFindTests tests = testGroup "get"
13211373 [ testGroup "definition" $ mapMaybe fst tests
1322− , testGroup "hover" $ mapMaybe snd tests ]
1374+ , testGroup "hover" $ mapMaybe snd tests
1375+ , checkFileCompiles sourceFilePath ]
13231376
1324− test runDef runHover look expect title =
1377+ test runDef runHover look expect = testM runDef runHover look (return expect)
1378+
1379+ testM runDef runHover look expect title =
13251380 ( runDef $ tst def look expect title
13261381 , runHover $ tst hover look expect title ) where
13271382 def = (getDefinitions, checkDefs)
@@ -1329,87 +1384,100 @@ findDefinitionAndHoverTests = let
13291384 --type_ = (getTypeDefinitions, checkTDefs) -- getTypeDefinitions always times out
13301385
13311386 -- search locations expectations on results
1332− fffL4 = _start fffR ; fffR = mkRange 4 4 4 7 ; fff = [ExpectRange fffR]
1333− fffL8 = Position 8 4 ;
1334− fffL14 = Position 14 7 ;
1335− aaaL14 = Position 14 20 ; aaa = [mkR 7 0 7 3]
1336− dcL7 = Position 7 11 ; tcDC = [mkR 3 23 5 16]
1337− dcL12 = Position 12 11 ;
1338− xtcL5 = Position 5 11 ; xtc = [ExpectExternFail, ExpectHoverText ["Int", "Defined in ‘GHC.Types’"]]
1339− tcL6 = Position 6 11 ; tcData = [mkR 3 0 5 16, ExpectHoverText ["TypeConstructor", "GotoHover.hs:4:1"]]
1340− vvL16 = Position 16 12 ; vv = [mkR 16 4 16 6]
1341− opL16 = Position 16 15 ; op = [mkR 17 2 17 4]
1342− opL18 = Position 18 22 ; opp = [mkR 18 13 18 17]
1343− aL18 = Position 18 20 ; apmp = [mkR 18 10 18 11]
1344− b'L19 = Position 19 13 ; bp = [mkR 19 6 19 7]
1345− xvL20 = Position 20 8 ; xvMsg = [ExpectExternFail, ExpectHoverText ["Data.Text.pack", ":: String -> Text"]]
1346− clL23 = Position 23 11 ; cls = [mkR 21 0 22 20, ExpectHoverText ["MyClass", "GotoHover.hs:22:1"]]
1347− clL25 = Position 25 9
1348− eclL15 = Position 15 8 ; ecls = [ExpectExternFail, ExpectHoverText ["Num", "Defined in ‘GHC.Num’"]]
1349− dnbL29 = Position 29 18 ; dnb = [ExpectHoverText [":: ()"], mkR 29 12 29 21]
1350− dnbL30 = Position 30 23
1351− lcbL33 = Position 33 26 ; lcb = [ExpectHoverText [":: Char"], mkR 33 26 33 27]
1352− lclL33 = Position 33 22
1353− mclL36 = Position 36 1 ; mcl = [mkR 36 0 36 14]
1354− mclL37 = Position 37 1
1355− spaceL37 = Position 37 24 ; space = [ExpectNoDefinitions, ExpectHoverText [":: Char"]]
1356− docL41 = Position 41 1 ; doc = [ExpectHoverText ["Recognizable docs: kpqz"]]
1387+ fffL4 = _start fffR ; fffR = mkRange 8 4 8 7 ; fff = [ExpectRange fffR]
1388+ fffL8 = Position 12 4 ;
1389+ fffL14 = Position 18 7 ;
1390+ aaaL14 = Position 18 20 ; aaa = [mkR 11 0 11 3]
1391+ dcL7 = Position 11 11 ; tcDC = [mkR 7 23 9 16]
1392+ dcL12 = Position 16 11 ;
1393+ xtcL5 = Position 9 11 ; xtc = [ExpectExternFail, ExpectHoverText ["Int", "Defined in ‘GHC.Types’"]]
1394+ tcL6 = Position 10 11 ; tcData = [mkR 7 0 9 16, ExpectHoverText ["TypeConstructor", "GotoHover.hs:8:1"]]
1395+ vvL16 = Position 20 12 ; vv = [mkR 20 4 20 6]
1396+ opL16 = Position 20 15 ; op = [mkR 21 2 21 4]
1397+ opL18 = Position 22 22 ; opp = [mkR 22 13 22 17]
1398+ aL18 = Position 22 20 ; apmp = [mkR 22 10 22 11]
1399+ b'L19 = Position 23 13 ; bp = [mkR 23 6 23 7]
1400+ xvL20 = Position 24 8 ; xvMsg = [ExpectExternFail, ExpectHoverText ["Data.Text.pack", ":: String -> Text"]]
1401+ clL23 = Position 27 11 ; cls = [mkR 25 0 26 20, ExpectHoverText ["MyClass", "GotoHover.hs:26:1"]]
1402+ clL25 = Position 29 9
1403+ eclL15 = Position 19 8 ; ecls = [ExpectExternFail, ExpectHoverText ["Num", "Defined in ‘GHC.Num’"]]
1404+ dnbL29 = Position 33 18 ; dnb = [ExpectHoverText [":: ()"], mkR 33 12 33 21]
1405+ dnbL30 = Position 34 23
1406+ lcbL33 = Position 37 26 ; lcb = [ExpectHoverText [":: Char"], mkR 37 26 37 27]
1407+ lclL33 = Position 37 22
1408+ mclL36 = Position 40 1 ; mcl = [mkR 40 0 40 14]
1409+ mclL37 = Position 41 1
1410+ spaceL37 = Position 41 24 ; space = [ExpectNoDefinitions, ExpectHoverText [":: Char"]]
1411+ docL41 = Position 45 1 ; doc = [ExpectHoverText ["Recognizable docs: kpqz"]]
13571412 ; constr = [ExpectHoverText ["Monad m"]]
1358− eitL40 = Position 40 28 ; kindE = [ExpectHoverText [":: * -> * -> *\n"]]
1359− intL40 = Position 40 34 ; kindI = [ExpectHoverText [":: *\n"]]
1360− tvrL40 = Position 40 37 ; kindV = [ExpectHoverText [":: * -> *\n"]]
1361− intL41 = Position 41 20 ; litI = [ExpectHoverText ["7518"]]
1362− chrL36 = Position 37 24 ; litC = [ExpectHoverText ["'f'"]]
1363− txtL8 = Position 8 14 ; litT = [ExpectHoverText ["\"dfgy\""]]
1364− lstL43 = Position 43 12 ; litL = [ExpectHoverText ["[8391 :: Int, 6268]"]]
1365− outL45 = Position 45 3 ; outSig = [ExpectHoverText ["outer", "Bool"], mkR 46 0 46 5]
1366− innL48 = Position 48 5 ; innSig = [ExpectHoverText ["inner", "Char"], mkR 49 2 49 7]
1413+ eitL40 = Position 44 28 ; kindE = [ExpectHoverText [":: * -> * -> *\n"]]
1414+ intL40 = Position 44 34 ; kindI = [ExpectHoverText [":: *\n"]]
1415+ tvrL40 = Position 44 37 ; kindV = [ExpectHoverText [":: * -> *\n"]]
1416+ intL41 = Position 45 20 ; litI = [ExpectHoverText ["7518"]]
1417+ chrL36 = Position 41 24 ; litC = [ExpectHoverText ["'f'"]]
1418+ txtL8 = Position 12 14 ; litT = [ExpectHoverText ["\"dfgy\""]]
1419+ lstL43 = Position 47 12 ; litL = [ExpectHoverText ["[8391 :: Int, 6268]"]]
1420+ outL45 = Position 49 3 ; outSig = [ExpectHoverText ["outer", "Bool"], mkR 46 0 46 5]
1421+ innL48 = Position 52 5 ; innSig = [ExpectHoverText ["inner", "Char"], mkR 49 2 49 7]
1422+ imported = Position 56 13 ; importedSig = getDocUri "Foo.hs" >>= \foo -> return [ExpectHoverText ["foo", "Foo"], mkL foo 4 0 4 3]
1423+ reexported = Position 55 14 ; reexportedSig = getDocUri "Bar.hs" >>= \bar -> return [ExpectHoverText ["Bar", "Bar"], mkL bar 2 0 2 14]
13671424 in
13681425 mkFindTests
1369− -- def hover look expect
1370− [ test yes yes fffL4 fff "field in record definition"
1371− , test broken broken fffL8 fff "field in record construction #71"
1372− , test yes yes fffL14 fff "field name used as accessor" -- 120 in Calculate.hs
1373− , test yes yes aaaL14 aaa "top-level name" -- 120
1374− , test yes yes dcL7 tcDC "data constructor record #247"
1375− , test yes yes dcL12 tcDC "data constructor plain" -- 121
1376− , test yes yes tcL6 tcData "type constructor #248" -- 147
1377− , test broken yes xtcL5 xtc "type constructor external #248,249"
1378− , test broken yes xvL20 xvMsg "value external package #249" -- 120
1379− , test yes yes vvL16 vv "plain parameter" -- 120
1380− , test yes yes aL18 apmp "pattern match name" -- 120
1381− , test yes yes opL16 op "top-level operator" -- 120, 123
1382− , test yes yes opL18 opp "parameter operator" -- 120
1383− , test yes yes b'L19 bp "name in backticks" -- 120
1384− , test yes yes clL23 cls "class in instance declaration #250"
1385− , test yes yes clL25 cls "class in signature #250" -- 147
1386− , test broken yes eclL15 ecls "external class in signature #249,250"
1387− , test yes yes dnbL29 dnb "do-notation bind" -- 137
1388− , test yes yes dnbL30 dnb "do-notation lookup"
1389− , test yes yes lcbL33 lcb "listcomp bind" -- 137
1390− , test yes yes lclL33 lcb "listcomp lookup"
1391− , test yes yes mclL36 mcl "top-level fn 1st clause"
1392− , test yes yes mclL37 mcl "top-level fn 2nd clause #246"
1393− , test yes yes spaceL37 space "top-level fn on space #315"
1394− , test no broken docL41 doc "documentation #7"
1395− , test no yes eitL40 kindE "kind of Either #273"
1396− , test no yes intL40 kindI "kind of Int #273"
1397− , test no broken tvrL40 kindV "kind of (* -> *) type variable #273"
1398− , test no yes intL41 litI "literal Int in hover info #274"
1399− , test no yes chrL36 litC "literal Char in hover info #274"
1400− , test no yes txtL8 litT "literal Text in hover info #274"
1401− , test no yes lstL43 litL "literal List in hover info #274"
1402− , test no yes docL41 constr "type constraint in hover info #283"
1403− , test broken broken outL45 outSig "top-level signature #310"
1404− , test broken broken innL48 innSig "inner signature #310"
1426+ -- def hover look expect
1427+ [ test yes yes fffL4 fff "field in record definition"
1428+ , test broken broken fffL8 fff "field in record construction #71"
1429+ , test yes yes fffL14 fff "field name used as accessor" -- 120 in Calculate.hs
1430+ , test yes yes aaaL14 aaa "top-level name" -- 120
1431+ , test yes yes dcL7 tcDC "data constructor record #247"
1432+ , test yes yes dcL12 tcDC "data constructor plain" -- 121
1433+ , test yes yes tcL6 tcData "type constructor #248" -- 147
1434+ , test broken yes xtcL5 xtc "type constructor external #248,249"
1435+ , test broken yes xvL20 xvMsg "value external package #249" -- 120
1436+ , test yes yes vvL16 vv "plain parameter" -- 120
1437+ , test yes yes aL18 apmp "pattern match name" -- 120
1438+ , test yes yes opL16 op "top-level operator" -- 120, 123
1439+ , test yes yes opL18 opp "parameter operator" -- 120
1440+ , test yes yes b'L19 bp "name in backticks" -- 120
1441+ , test yes yes clL23 cls "class in instance declaration #250"
1442+ , test yes yes clL25 cls "class in signature #250" -- 147
1443+ , test broken yes eclL15 ecls "external class in signature #249,250"
1444+ , test yes yes dnbL29 dnb "do-notation bind" -- 137
1445+ , test yes yes dnbL30 dnb "do-notation lookup"
1446+ , test yes yes lcbL33 lcb "listcomp bind" -- 137
1447+ , test yes yes lclL33 lcb "listcomp lookup"
1448+ , test yes yes mclL36 mcl "top-level fn 1st clause"
1449+ , test yes yes mclL37 mcl "top-level fn 2nd clause #246"
1450+ , test yes yes spaceL37 space "top-level fn on space #315"
1451+ , test no broken docL41 doc "documentation #7"
1452+ , test no yes eitL40 kindE "kind of Either #273"
1453+ , test no yes intL40 kindI "kind of Int #273"
1454+ , test no broken tvrL40 kindV "kind of (* -> *) type variable #273"
1455+ , test no yes intL41 litI "literal Int in hover info #274"
1456+ , test no yes chrL36 litC "literal Char in hover info #274"
1457+ , test no yes txtL8 litT "literal Text in hover info #274"
1458+ , test no yes lstL43 litL "literal List in hover info #274"
1459+ , test no yes docL41 constr "type constraint in hover info #283"
1460+ , test broken broken outL45 outSig "top-level signature #310"
1461+ , test broken broken innL48 innSig "inner signature #310"
1462+ , testM yes yes imported importedSig "Imported symbol"
1463+ , testM yes yes reexported reexportedSig "Imported symbol (reexported)"
14051464 ]
14061465 where yes, broken :: (TestTree -> Maybe TestTree)
14071466 yes = Just -- test should run and pass
14081467 broken = Just . (`xfail` "known broken")
14091468 no = const Nothing -- don't run this test at all
14101469
1470+checkFileCompiles :: FilePath -> TestTree
1471+checkFileCompiles fp =
1472+ testSessionWithExtraFiles "hover" ("Does " ++ fp ++ " compile") $ \dir -> do
1473+ void (openTestDataDoc (dir </> fp))
1474+ expectNoMoreDiagnostics 0.5
1475+
1476+
1477+
14111478pluginTests :: TestTree
1412−pluginTests = testSessionWait "plugins" $ do
1479+pluginTests = (`xfail8101` "known broken (#556)")
1480+ $ testSessionWait "plugins" $ do
14131481 let content =
14141482 T.unlines
14151483 [ "{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}"
@@ -1423,7 +1491,7 @@ pluginTests = testSessionWait "plugins" $ do
14231491 , "foo :: Int -> Int -> Int"
14241492 , "foo a b = a + c"
14251493 ]
1426− _ <- openDoc' "Testing.hs" "haskell" content
1494+ _ <- createDoc "Testing.hs" "haskell" content
14271495 expectDiagnostics
14281496 [ ( "Testing.hs",
14291497 [(DsError, (8, 14), "Variable not in scope: c")]
@@ -1450,7 +1518,7 @@ cppTests =
14501518 run $ expectError content (2, 1)
14511519 )
14521520 , testSessionWait "cpp-ghcide" $ do
1453− _ <- openDoc' "A.hs" "haskell" $ T.unlines
1521+ _ <- createDoc "A.hs" "haskell" $ T.unlines
14541522 ["{-# LANGUAGE CPP #-}"
14551523 ,"main ="
14561524 ,"#ifdef __GHCIDE__"
@@ -1464,7 +1532,7 @@ cppTests =
14641532 where
14651533 expectError :: T.Text -> Cursor -> Session ()
14661534 expectError content cursor = do
1467− _ <- openDoc' "Testing.hs" "haskell" content
1535+ _ <- createDoc "Testing.hs" "haskell" content
14681536 expectDiagnostics
14691537 [ ( "Testing.hs",
14701538 [(DsError, cursor, "error: unterminated")]
@@ -1480,7 +1548,7 @@ preprocessorTests = testSessionWait "preprocessor" $ do
14801548 , "module Testing where"
14811549 , "y = x + z" -- plugin replaces x with y, making this have only one diagnostic
14821550 ]
1483− _ <- openDoc' "Testing.hs" "haskell" content
1551+ _ <- createDoc "Testing.hs" "haskell" content
14841552 expectDiagnostics
14851553 [ ( "Testing.hs",
14861554 [(DsError, (2, 8), "Variable not in scope: z")]
@@ -1513,8 +1581,8 @@ safeTests =
15131581 ,"safeId = trustWorthyId"
15141582 ]
15151583
1516− _ <- openDoc' "A.hs" "haskell" sourceA
1517− _ <- openDoc' "B.hs" "haskell" sourceB
1584+ _ <- createDoc "A.hs" "haskell" sourceA
1585+ _ <- createDoc "B.hs" "haskell" sourceB
15181586 expectNoMoreDiagnostics 1 ]
15191587
15201588thTests :: TestTree
@@ -1542,8 +1610,8 @@ thTests =
15421610 "b :: Integer",
15431611 "b = $(litE $ IntegerL $ a) + n"
15441612 ]
1545− _ <- openDoc' "A.hs" "haskell" sourceA
1546− _ <- openDoc' "B.hs" "haskell" sourceB
1613+ _ <- createDoc "A.hs" "haskell" sourceA
1614+ _ <- createDoc "B.hs" "haskell" sourceB
15471615 expectDiagnostics [ ( "B.hs", [(DsError, (6, 29), "Variable not in scope: n")] ) ]
15481616 , testSessionWait "newtype-closure" $ do
15491617 let sourceA =
@@ -1563,8 +1631,8 @@ thTests =
15631631 ,"import A"
15641632 ,"b :: Int"
15651633 ,"b = $( a )" ]
1566− _ <- openDoc' "A.hs" "haskell" sourceA
1567− _ <- openDoc' "B.hs" "haskell" sourceB
1634+ _ <- createDoc "A.hs" "haskell" sourceA
1635+ _ <- createDoc "B.hs" "haskell" sourceB
15681636 return ()
15691637 ]
15701638
@@ -1573,7 +1641,7 @@ completionTests
15731641 = testGroup "completion"
15741642 [ testSessionWait "variable" $ do
15751643 let source = T.unlines ["module A where", "f = hea"]
1576− docId <- openDoc' "A.hs" "haskell" source
1644+ docId <- createDoc "A.hs" "haskell" source
15771645 compls <- getCompletions docId (Position 1 7)
15781646 liftIO $ map dropDocs compls @?=
15791647 [complItem "head" (Just CiFunction) (Just "[a] -> a")]
@@ -1585,7 +1653,7 @@ completionTests
15851653 ]
15861654 , testSessionWait "constructor" $ do
15871655 let source = T.unlines ["module A where", "f = Tru"]
1588− docId <- openDoc' "A.hs" "haskell" source
1656+ docId <- createDoc "A.hs" "haskell" source
15891657 compls <- getCompletions docId (Position 1 7)
15901658 liftIO $ map dropDocs compls @?=
15911659 [ complItem "True" (Just CiConstructor) (Just "Bool")
@@ -1597,7 +1665,7 @@ completionTests
15971665 ]
15981666 , testSessionWait "type" $ do
15991667 let source = T.unlines ["{-# OPTIONS_GHC -Wall #-}", "module A () where", "f :: ()", "f = ()"]
1600− docId <- openDoc' "A.hs" "haskell" source
1668+ docId <- createDoc "A.hs" "haskell" source
16011669 expectDiagnostics [ ("A.hs", [(DsWarning, (3,0), "not used")]) ]
16021670 changeDoc docId [TextDocumentContentChangeEvent Nothing Nothing $ T.unlines ["{-# OPTIONS_GHC -Wall #-}", "module A () where", "f :: Bo", "f = True"]]
16031671 compls <- getCompletions docId (Position 2 7)
@@ -1614,7 +1682,7 @@ completionTests
16141682 checkDocText "Bool" boolDocs [ "Defined in 'Prelude'" ]
16151683 , testSessionWait "qualified" $ do
16161684 let source = T.unlines ["{-# OPTIONS_GHC -Wunused-binds #-}", "module A () where", "f = ()"]
1617− docId <- openDoc' "A.hs" "haskell" source
1685+ docId <- createDoc "A.hs" "haskell" source
16181686 expectDiagnostics [ ("A.hs", [(DsWarning, (2, 0), "not used")]) ]
16191687 changeDoc docId [TextDocumentContentChangeEvent Nothing Nothing $ T.unlines ["{-# OPTIONS_GHC -Wunused-binds #-}", "module A () where", "f = Prelude.hea"]]
16201688 compls <- getCompletions docId (Position 2 15)
@@ -1628,7 +1696,7 @@ completionTests
16281696 ]
16291697 , testSessionWait "keyword" $ do
16301698 let source = T.unlines ["module A where", "f = newty"]
1631− docId <- openDoc' "A.hs" "haskell" source
1699+ docId <- createDoc "A.hs" "haskell" source
16321700 compls <- getCompletions docId (Position 1 9)
16331701 liftIO $ compls @?= [keywordItem "newtype"]
16341702 , testSessionWait "type context" $ do
@@ -1637,7 +1705,7 @@ completionTests
16371705 , "module A () where"
16381706 , "f = f"
16391707 ]
1640− docId <- openDoc' "A.hs" "haskell" source
1708+ docId <- createDoc "A.hs" "haskell" source
16411709 expectDiagnostics [("A.hs", [(DsWarning, (2, 0), "not used")])]
16421710 changeDoc docId
16431711 [ TextDocumentContentChangeEvent Nothing Nothing $ T.unlines
@@ -1705,7 +1773,7 @@ outlineTests = testGroup
17051773 "outline"
17061774 [ testSessionWait "type class" $ do
17071775 let source = T.unlines ["module A where", "class A a where a :: a -> Bool"]
1708− docId <- openDoc' "A.hs" "haskell" source
1776+ docId <- createDoc "A.hs" "haskell" source
17091777 symbols <- getDocumentSymbols docId
17101778 liftIO $ symbols @?= Left
17111779 [ moduleSymbol
@@ -1718,7 +1786,7 @@ outlineTests = testGroup
17181786 ]
17191787 , testSessionWait "type class instance " $ do
17201788 let source = T.unlines ["class A a where", "instance A () where"]
1721− docId <- openDoc' "A.hs" "haskell" source
1789+ docId <- createDoc "A.hs" "haskell" source
17221790 symbols <- getDocumentSymbols docId
17231791 liftIO $ symbols @?= Left
17241792 [ classSymbol "A a" (R 0 0 0 15) []
@@ -1726,7 +1794,7 @@ outlineTests = testGroup
17261794 ]
17271795 , testSessionWait "type family" $ do
17281796 let source = T.unlines ["{-# language TypeFamilies #-}", "type family A"]
1729− docId <- openDoc' "A.hs" "haskell" source
1797+ docId <- createDoc "A.hs" "haskell" source
17301798 symbols <- getDocumentSymbols docId
17311799 liftIO $ symbols @?= Left [docSymbolD "A" "type family" SkClass (R 1 0 1 13)]
17321800 , testSessionWait "type family instance " $ do
@@ -1735,7 +1803,7 @@ outlineTests = testGroup
17351803 , "type family A a"
17361804 , "type instance A () = ()"
17371805 ]
1738− docId <- openDoc' "A.hs" "haskell" source
1806+ docId <- createDoc "A.hs" "haskell" source
17391807 symbols <- getDocumentSymbols docId
17401808 liftIO $ symbols @?= Left
17411809 [ docSymbolD "A a" "type family" SkClass (R 1 0 1 15)
@@ -1743,7 +1811,7 @@ outlineTests = testGroup
17431811 ]
17441812 , testSessionWait "data family" $ do
17451813 let source = T.unlines ["{-# language TypeFamilies #-}", "data family A"]
1746− docId <- openDoc' "A.hs" "haskell" source
1814+ docId <- createDoc "A.hs" "haskell" source
17471815 symbols <- getDocumentSymbols docId
17481816 liftIO $ symbols @?= Left [docSymbolD "A" "data family" SkClass (R 1 0 1 11)]
17491817 , testSessionWait "data family instance " $ do
@@ -1752,7 +1820,7 @@ outlineTests = testGroup
17521820 , "data family A a"
17531821 , "data instance A () = A ()"
17541822 ]
1755− docId <- openDoc' "A.hs" "haskell" source
1823+ docId <- createDoc "A.hs" "haskell" source
17561824 symbols <- getDocumentSymbols docId
17571825 liftIO $ symbols @?= Left
17581826 [ docSymbolD "A a" "data family" SkClass (R 1 0 1 11)
@@ -1760,36 +1828,36 @@ outlineTests = testGroup
17601828 ]
17611829 , testSessionWait "constant" $ do
17621830 let source = T.unlines ["a = ()"]
1763− docId <- openDoc' "A.hs" "haskell" source
1831+ docId <- createDoc "A.hs" "haskell" source
17641832 symbols <- getDocumentSymbols docId
17651833 liftIO $ symbols @?= Left
17661834 [docSymbol "a" SkFunction (R 0 0 0 6)]
17671835 , testSessionWait "pattern" $ do
17681836 let source = T.unlines ["Just foo = Just 21"]
1769− docId <- openDoc' "A.hs" "haskell" source
1837+ docId <- createDoc "A.hs" "haskell" source
17701838 symbols <- getDocumentSymbols docId
17711839 liftIO $ symbols @?= Left
17721840 [docSymbol "Just foo" SkFunction (R 0 0 0 18)]
17731841 , testSessionWait "pattern with type signature" $ do
17741842 let source = T.unlines ["{-# language ScopedTypeVariables #-}", "a :: () = ()"]
1775− docId <- openDoc' "A.hs" "haskell" source
1843+ docId <- createDoc "A.hs" "haskell" source
17761844 symbols <- getDocumentSymbols docId
17771845 liftIO $ symbols @?= Left
17781846 [docSymbol "a :: ()" SkFunction (R 1 0 1 12)]
17791847 , testSessionWait "function" $ do
17801848 let source = T.unlines ["a x = ()"]
1781− docId <- openDoc' "A.hs" "haskell" source
1849+ docId <- createDoc "A.hs" "haskell" source
17821850 symbols <- getDocumentSymbols docId
17831851 liftIO $ symbols @?= Left [docSymbol "a" SkFunction (R 0 0 0 8)]
17841852 , testSessionWait "type synonym" $ do
17851853 let source = T.unlines ["type A = Bool"]
1786− docId <- openDoc' "A.hs" "haskell" source
1854+ docId <- createDoc "A.hs" "haskell" source
17871855 symbols <- getDocumentSymbols docId
17881856 liftIO $ symbols @?= Left
17891857 [docSymbol' "A" SkTypeParameter (R 0 0 0 13) (R 0 5 0 6)]
17901858 , testSessionWait "datatype" $ do
17911859 let source = T.unlines ["data A = C"]
1792− docId <- openDoc' "A.hs" "haskell" source
1860+ docId <- createDoc "A.hs" "haskell" source
17931861 symbols <- getDocumentSymbols docId
17941862 liftIO $ symbols @?= Left
17951863 [ docSymbolWithChildren "A"
@@ -1797,9 +1865,21 @@ outlineTests = testGroup
17971865 (R 0 0 0 10)
17981866 [docSymbol "C" SkConstructor (R 0 9 0 10)]
17991867 ]
1868+ , testSessionWait "record fields" $ do
1869+ let source = T.unlines ["data A = B {", " x :: Int", " , y :: Int}"]
1870+ docId <- createDoc "A.hs" "haskell" source
1871+ symbols <- getDocumentSymbols docId
1872+ liftIO $ symbols @=? Left
1873+ [ docSymbolWithChildren "A" SkStruct (R 0 0 2 13)
1874+ [ docSymbolWithChildren' "B" SkConstructor (R 0 9 2 13) (R 0 9 0 10)
1875+ [ docSymbol "x" SkField (R 1 2 1 3)
1876+ , docSymbol "y" SkField (R 2 4 2 5)
1877+ ]
1878+ ]
1879+ ]
18001880 , testSessionWait "import" $ do
18011881 let source = T.unlines ["import Data.Maybe"]
1802− docId <- openDoc' "A.hs" "haskell" source
1882+ docId <- createDoc "A.hs" "haskell" source
18031883 symbols <- getDocumentSymbols docId
18041884 liftIO $ symbols @?= Left
18051885 [docSymbolWithChildren "imports"
@@ -1810,7 +1890,7 @@ outlineTests = testGroup
18101890 ]
18111891 , testSessionWait "multiple import" $ do
18121892 let source = T.unlines ["", "import Data.Maybe", "", "import Control.Exception", ""]
1813− docId <- openDoc' "A.hs" "haskell" source
1893+ docId <- createDoc "A.hs" "haskell" source
18141894 symbols <- getDocumentSymbols docId
18151895 liftIO $ symbols @?= Left
18161896 [docSymbolWithChildren "imports"
@@ -1825,7 +1905,7 @@ outlineTests = testGroup
18251905 [ "{-# language ForeignFunctionInterface #-}"
18261906 , "foreign import ccall \"a\" a :: Int"
18271907 ]
1828− docId <- openDoc' "A.hs" "haskell" source
1908+ docId <- createDoc "A.hs" "haskell" source
18291909 symbols <- getDocumentSymbols docId
18301910 liftIO $ symbols @?= Left [docSymbolD "a" "import" SkObject (R 1 0 1 33)]
18311911 , testSessionWait "foreign export" $ do
@@ -1833,7 +1913,7 @@ outlineTests = testGroup
18331913 [ "{-# language ForeignFunctionInterface #-}"
18341914 , "foreign export ccall odd :: Int -> Bool"
18351915 ]
1836− docId <- openDoc' "A.hs" "haskell" source
1916+ docId <- createDoc "A.hs" "haskell" source
18371917 symbols <- getDocumentSymbols docId
18381918 liftIO $ symbols @?= Left [docSymbolD "odd" "export" SkObject (R 1 0 1 39)]
18391919 ]
@@ -1846,6 +1926,8 @@ outlineTests = testGroup
18461926 DocumentSymbol name (Just detail) kind Nothing loc loc Nothing
18471927 docSymbolWithChildren name kind loc cc =
18481928 DocumentSymbol name Nothing kind Nothing loc loc (Just $ List cc)
1929+ docSymbolWithChildren' name kind loc selectionLoc cc =
1930+ DocumentSymbol name Nothing kind Nothing loc selectionLoc (Just $ List cc)
18491931 moduleSymbol name loc cc = DocumentSymbol name
18501932 Nothing
18511933 SkFile
@@ -1867,8 +1949,16 @@ pattern R x y x' y' = Range (Position x y) (Position x' y')
18671949xfail :: TestTree -> String -> TestTree
18681950xfail = flip expectFailBecause
18691951
1952+xfail8101 :: TestTree -> String -> TestTree
1953+#if MIN_GHC_API_VERSION(8,10,0)
1954+xfail8101 = flip expectFailBecause
1955+#else
1956+xfail8101 t _ = t
1957+#endif
1958+
18701959data Expect
18711960 = ExpectRange Range -- Both gotoDef and hover should report this range
1961+ | ExpectLocation Location
18721962-- | ExpectDefRange Range -- Only gotoDef should report this range
18731963 | ExpectHoverRange Range -- Only hover should report this range
18741964 | ExpectHoverText [T.Text] -- the hover message must contain these snippets
@@ -1881,6 +1971,9 @@ data Expect
18811971mkR :: Int -> Int -> Int -> Int -> Expect
18821972mkR startLine startColumn endLine endColumn = ExpectRange $ mkRange startLine startColumn endLine endColumn
18831973
1974+mkL :: Uri -> Int -> Int -> Int -> Int -> Expect
1975+mkL uri startLine startColumn endLine endColumn = ExpectLocation $ Location uri $ mkRange startLine startColumn endLine endColumn
1976+
18841977haddockTests :: TestTree
18851978haddockTests
18861979 = testGroup "haddock"
@@ -1940,6 +2033,7 @@ cradleTests :: TestTree
19402033cradleTests = testGroup "cradle"
19412034 [testGroup "dependencies" [sessionDepsArePickedUp]
19422035 ,testGroup "loading" [loadCradleOnlyonce]
2036+ ,testGroup "multi" [simpleMultiTest, simpleMultiTest2]
19432037 ]
19442038
19452039loadCradleOnlyonce :: TestTree
@@ -1954,17 +2048,53 @@ loadCradleOnlyonce = testGroup "load cradle only once"
19542048 test dir
19552049 implicit dir = test dir
19562050 test _dir = do
1957− doc <- openDoc' "B.hs" "haskell" "module B where\nimport Data.Foo"
2051+ doc <- createDoc "B.hs" "haskell" "module B where\nimport Data.Foo"
19582052 msgs <- someTill (skipManyTill anyMessage cradleLoadedMessage) (skipManyTill anyMessage (message @PublishDiagnosticsNotification))
19592053 liftIO $ length msgs @?= 1
19602054 changeDoc doc [TextDocumentContentChangeEvent Nothing Nothing "module B where\nimport Data.Maybe"]
19612055 msgs <- manyTill (skipManyTill anyMessage cradleLoadedMessage) (skipManyTill anyMessage (message @PublishDiagnosticsNotification))
19622056 liftIO $ length msgs @?= 0
1963− _ <- openDoc' "A.hs" "haskell" "module A where\nimport Bar"
2057+ _ <- createDoc "A.hs" "haskell" "module A where\nimport LoadCradleBar"
19642058 msgs <- manyTill (skipManyTill anyMessage cradleLoadedMessage) (skipManyTill anyMessage (message @PublishDiagnosticsNotification))
19652059 liftIO $ length msgs @?= 0
19662060
19672061
2062+dependentFileTest :: TestTree
2063+dependentFileTest = testGroup "addDependentFile"
2064+ [testGroup "file-changed" [testSession' "test" test]
2065+ ]
2066+ where
2067+ test dir = do
2068+ -- If the file contains B then no type error
2069+ -- otherwise type error
2070+ liftIO $ writeFile (dir </> "dep-file.txt") "A"
2071+ let fooContent = T.unlines
2072+ [ "{-# LANGUAGE TemplateHaskell #-}"
2073+ , "module Foo where"
2074+ , "import Language.Haskell.TH.Syntax"
2075+ , "foo :: Int"
2076+ , "foo = 1 + $(do"
2077+ , " qAddDependentFile \"dep-file.txt\""
2078+ , " f <- qRunIO (readFile \"dep-file.txt\")"
2079+ , " if f == \"B\" then [| 1 |] else lift f)"
2080+ ]
2081+ let bazContent = T.unlines ["module Baz where", "import Foo"]
2082+ _ <-createDoc "Foo.hs" "haskell" fooContent
2083+ doc <- createDoc "Baz.hs" "haskell" bazContent
2084+ expectDiagnostics
2085+ [("Foo.hs", [(DsError, (4, 6), "Couldn't match expected type")])]
2086+ -- Now modify the dependent file
2087+ liftIO $ writeFile (dir </> "dep-file.txt") "B"
2088+ let change = TextDocumentContentChangeEvent
2089+ { _range = Just (Range (Position 2 0) (Position 2 6))
2090+ , _rangeLength = Nothing
2091+ , _text = "f = ()"
2092+ }
2093+ -- Modifying Baz will now trigger Foo to be rebuilt as well
2094+ changeDoc doc [change]
2095+ expectDiagnostics [("Foo.hs", [])]
2096+
2097+
19682098cradleLoadedMessage :: Session FromServerMessage
19692099cradleLoadedMessage = satisfy $ \case
19702100 NotCustomServer (NotificationMessage _ (CustomServerMethod m) _) -> m == cradleLoadedMethod
@@ -1973,6 +2103,56 @@ cradleLoadedMessage = satisfy $ \case
19732103cradleLoadedMethod :: T.Text
19742104cradleLoadedMethod = "ghcide/cradle/loaded"
19752105
2106+-- Stack sets this which trips up cabal in the multi-component tests.
2107+-- However, our plugin tests rely on those env vars so we unset it locally.
2108+withoutStackEnv :: IO a -> IO a
2109+withoutStackEnv s =
2110+ bracket
2111+ (mapM getEnv vars >>= \prevState -> mapM_ unsetEnv vars >> pure prevState)
2112+ (\prevState -> mapM_ (\(var, value) -> restore var value) (zip vars prevState))
2113+ (const s)
2114+ where vars =
2115+ [ "GHC_PACKAGE_PATH"
2116+ , "GHC_ENVIRONMENT"
2117+ , "HASKELL_DIST_DIR"
2118+ , "HASKELL_PACKAGE_SANDBOX"
2119+ , "HASKELL_PACKAGE_SANDBOXES"
2120+ ]
2121+ restore var Nothing = unsetEnv var
2122+ restore var (Just val) = setEnv var val True
2123+
2124+simpleMultiTest :: TestTree
2125+simpleMultiTest = testCase "simple-multi-test" $ withoutStackEnv $ runWithExtraFiles "multi" $ \dir -> do
2126+ let aPath = dir </> "a/A.hs"
2127+ bPath = dir </> "b/B.hs"
2128+ aSource <- liftIO $ readFileUtf8 aPath
2129+ (TextDocumentIdentifier adoc) <- createDoc aPath "haskell" aSource
2130+ expectNoMoreDiagnostics 0.5
2131+ bSource <- liftIO $ readFileUtf8 bPath
2132+ bdoc <- createDoc bPath "haskell" bSource
2133+ expectNoMoreDiagnostics 0.5
2134+ locs <- getDefinitions bdoc (Position 2 7)
2135+ let fooL = mkL adoc 2 0 2 3
2136+ checkDefs locs (pure [fooL])
2137+ expectNoMoreDiagnostics 0.5
2138+
2139+-- Like simpleMultiTest but open the files in the other order
2140+simpleMultiTest2 :: TestTree
2141+simpleMultiTest2 = testCase "simple-multi-test2" $ withoutStackEnv $ runWithExtraFiles "multi" $ \dir -> do
2142+ let aPath = dir </> "a/A.hs"
2143+ bPath = dir </> "b/B.hs"
2144+ bSource <- liftIO $ readFileUtf8 bPath
2145+ bdoc <- createDoc bPath "haskell" bSource
2146+ expectNoMoreDiagnostics 5
2147+ aSource <- liftIO $ readFileUtf8 aPath
2148+ (TextDocumentIdentifier adoc) <- createDoc aPath "haskell" aSource
2149+ -- Need to have some delay here or the test fails
2150+ expectNoMoreDiagnostics 5
2151+ locs <- getDefinitions bdoc (Position 2 7)
2152+ let fooL = mkL adoc 2 0 2 3
2153+ checkDefs locs (pure [fooL])
2154+ expectNoMoreDiagnostics 0.5
2155+
19762156sessionDepsArePickedUp :: TestTree
19772157sessionDepsArePickedUp = testSession'
19782158 "session-deps-are-picked-up"
@@ -1982,7 +2162,7 @@ sessionDepsArePickedUp = testSession'
19822162 (dir </> "hie.yaml")
19832163 "cradle: {direct: {arguments: []}}"
19842164 -- Open without OverloadedStrings and expect an error.
1985− doc <- openDoc' "Foo.hs" "haskell" fooContent
2165+ doc <- createDoc "Foo.hs" "haskell" fooContent
19862166 expectDiagnostics
19872167 [("Foo.hs", [(DsError, (3, 6), "Couldn't match expected type")])]
19882168 -- Update hie.yaml to enable OverloadedStrings.
@@ -2017,6 +2197,9 @@ sessionDepsArePickedUp = testSession'
20172197testSession :: String -> Session () -> TestTree
20182198testSession name = testCase name . run
20192199
2200+testSessionWithExtraFiles :: FilePath -> String -> (FilePath -> Session ()) -> TestTree
2201+testSessionWithExtraFiles prefix name = testCase name . runWithExtraFiles prefix
2202+
20202203testSession' :: String -> (FilePath -> Session ()) -> TestTree
20212204testSession' name = testCase name . run'
20222205
@@ -2051,6 +2234,19 @@ mkRange a b c d = Range (Position a b) (Position c d)
20512234run :: Session a -> IO a
20522235run s = withTempDir $ \dir -> runInDir dir s
20532236
2237+runWithExtraFiles :: FilePath -> (FilePath -> Session a) -> IO a
2238+runWithExtraFiles prefix s = withTempDir $ \dir -> do
2239+ copyTestDataFiles dir prefix
2240+ runInDir dir (s dir)
2241+
2242+copyTestDataFiles :: FilePath -> FilePath -> IO ()
2243+copyTestDataFiles dir prefix = do
2244+ -- Copy all the test data files to the temporary workspace
2245+ testDataFiles <- getDirectoryFilesIO ("test/data" </> prefix) ["//*"]
2246+ for_ testDataFiles $ \f -> do
2247+ createDirectoryIfMissing True $ dir </> takeDirectory f
2248+ copyFile ("test/data" </> prefix </> f) (dir </> f)
2249+
20542250run' :: (FilePath -> Session a) -> IO a
20552251run' s = withTempDir $ \dir -> runInDir dir (s dir)
20562252
@@ -2062,6 +2258,7 @@ runInDir dir s = do
20622258 -- since the package import test creates "Data/List.hs", which otherwise has no physical home
20632259 createDirectoryIfMissing True $ dir ++ "/Data"
20642260
2261+
20652262 let cmd = unwords [ghcideExe, "--lsp", "--test", "--cwd", dir]
20662263 -- HIE calls getXgdDirectory which assumes that HOME is set.
20672264 -- Only sets HOME if it wasn't already set.
@@ -2079,7 +2276,7 @@ runInDir dir s = do
20792276openTestDataDoc :: FilePath -> Session TextDocumentIdentifier
20802277openTestDataDoc path = do
20812278 source <- liftIO $ readFileUtf8 $ "test/data" </> path
2082− openDoc' path "haskell" source
2279+ createDoc path "haskell" source
20832280
20842281findCodeActions :: TextDocumentIdentifier -> Range -> [T.Text] -> Session [CodeAction]
20852282findCodeActions doc range expectedTitles = do
@@ -2120,20 +2317,6 @@ unitTests = do
21202317 uriToFilePath' uri @?= Just ""
21212318 ]
21222319
2123−-- | Wrapper around 'LSPTest.openDoc'' that sends file creation events
2124−openDoc' :: FilePath -> String -> T.Text -> Session TextDocumentIdentifier
2125−openDoc' fp name contents = do
2126− res@(TextDocumentIdentifier uri) <- LSPTest.openDoc' fp name contents
2127− -- Needed as ghcide sets up and relies on WatchedFiles but lsp-test does not track them
2128− sendNotification WorkspaceDidChangeWatchedFiles (DidChangeWatchedFilesParams $ List [FileEvent uri FcCreated])
2129− return res
2130−
2131−-- | Version of 'LSPTest.openDoc'' that does not send WatchedFiles events for files outside the workspace
2132−openDoc'' :: FilePath -> String -> T.Text -> Session TextDocumentIdentifier
2133−-- At the moment this is just LSPTest.openDoc' but it may change in the future
2134−-- when/if lsp-test implements WatchedFiles
2135−openDoc'' = LSPTest.openDoc'
2136−
21372320positionMappingTests :: TestTree
21382321positionMappingTests =
21392322 testGroup "position mapping"
@@ -2303,11 +2486,11 @@ nthLine i r
23032486 | i >= Rope.rows r = error $ "Row number out of bounds: " <> show i <> "/" <> show (Rope.rows r)
23042487 | otherwise = Rope.takeWhile (/= '\n') $ fst $ Rope.splitAtLine 1 $ snd $ Rope.splitAtLine (i - 1) r
23052488
2306−getWatchedFilesSubscriptionsUntilProgressEnd :: Session [Maybe Value]
2307−getWatchedFilesSubscriptionsUntilProgressEnd = do
2308− msgs <- manyTill (Just <$> message @RegisterCapabilityRequest <|> Nothing <$ anyMessage) (message @WorkDoneProgressEndNotification)
2489+getWatchedFilesSubscriptionsUntil :: forall end . (FromJSON end, Typeable end) => Session [Maybe Value]
2490+getWatchedFilesSubscriptionsUntil = do
2491+ msgs <- manyTill (Just <$> message @RegisterCapabilityRequest <|> Nothing <$ anyMessage) (message @end)
23092492 return
23102493 [ args
2311− | Just (RequestMessage{_params = RegistrationParams (List regs)}) <- msgs
2494+ | Just RequestMessage{_params = RegistrationParams (List regs)} <- msgs
23122495 , Registration _id WorkspaceDidChangeWatchedFiles args <- regs
23132496 ]