@@ -1,6 +1,7 @@
1-- Copyright (c) 2019 The DAML Authors. All rights reserved.
1-- Copyright (c) 2019 The DAML Authors. All rights reserved.
2-- SPDX-License-Identifier: Apache-2.0
2-- SPDX-License-Identifier: Apache-2.0
4+{-# LANGUAGE AllowAmbiguousTypes #-}
4{-# LANGUAGE DuplicateRecordFields #-}
5{-# LANGUAGE DuplicateRecordFields #-}
5{-# LANGUAGE PatternSynonyms #-}
6{-# LANGUAGE PatternSynonyms #-}
6{-# LANGUAGE CPP #-}
7{-# LANGUAGE CPP #-}
@@ -9,31 +10,32 @@
9module Main (main) where
10module Main (main) where
11import Control.Applicative.Combinators
12import Control.Applicative.Combinators
12−import Control.Exception (catch)
13+import Control.Exception (bracket, catch)
14+import qualified Control.Lens as Lens
13import Control.Monad
15import Control.Monad
14import Control.Monad.IO.Class (liftIO)
16import Control.Monad.IO.Class (liftIO)
15−import Data.Aeson (Value)
17+import Data.Aeson (FromJSON, Value)
16−import Data.Char (toLower)
17import Data.Foldable
18import Data.Foldable
18−import Data.List
19+import Data.List.Extra
19import Data.Rope.UTF16 (Rope)
20import Data.Rope.UTF16 (Rope)
20import qualified Data.Rope.UTF16 as Rope
21import qualified Data.Rope.UTF16 as Rope
21import Development.IDE.Core.PositionMapping (fromCurrent, toCurrent)
22import Development.IDE.Core.PositionMapping (fromCurrent, toCurrent)
22import Development.IDE.GHC.Util
23import Development.IDE.GHC.Util
23import qualified Data.Text as T
24import qualified Data.Text as T
24import Development.IDE.Spans.Common
26import Development.IDE.Spans.Common
25import Development.IDE.Test
27import Development.IDE.Test
26import Development.IDE.Test.Runfiles
28import Development.IDE.Test.Runfiles
27import Development.IDE.Types.Location
29import Development.IDE.Types.Location
28import Development.Shake (getDirectoryFilesIO)
30import Development.Shake (getDirectoryFilesIO)
29−import qualified Language.Haskell.LSP.Test as LSPTest
31+import Language.Haskell.LSP.Test
30−import Language.Haskell.LSP.Test hiding (openDoc')
31import Language.Haskell.LSP.Messages
32import Language.Haskell.LSP.Messages
32import Language.Haskell.LSP.Types
33import Language.Haskell.LSP.Types
33import Language.Haskell.LSP.Types.Capabilities
34import Language.Haskell.LSP.Types.Capabilities
35+import qualified Language.Haskell.LSP.Types.Lens as Lsp (diagnostics, params, message)
34import Language.Haskell.LSP.VFS (applyChange)
36import Language.Haskell.LSP.VFS (applyChange)
35import Network.URI
37import Network.URI
36−import System.Environment.Blank (setEnv)
38+import System.Environment.Blank (getEnv, setEnv, unsetEnv)
37import System.FilePath
39import System.FilePath
38import System.IO.Extra
40import System.IO.Extra
39import System.Directory
41import System.Directory
@@ -47,31 +49,35 @@ import Test.Tasty.QuickCheck
47import Data.Maybe
49import Data.Maybe
49main :: IO ()
51main :: IO ()
53+ -- We mess with env vars so run single-threaded.
54+ setEnv "TASTY_NUM_THREADS" "1" True
50−main = defaultMainWithRerun $ testGroup "HIE"
55+ defaultMainWithRerun $ testGroup "HIE"
51− [ testSession "open close" $ do
56+ [ testSession "open close" $ do
52− doc <- openDoc' "Testing.hs" "haskell" ""
57+ doc <- createDoc "Testing.hs" "haskell" ""
53− void (skipManyTill anyMessage message :: Session WorkDoneProgressCreateRequest)
58+ void (skipManyTill anyMessage message :: Session WorkDoneProgressCreateRequest)
54− void (skipManyTill anyMessage message :: Session WorkDoneProgressBeginNotification)
59+ void (skipManyTill anyMessage message :: Session WorkDoneProgressBeginNotification)
55− closeDoc doc
60+ closeDoc doc
56− void (skipManyTill anyMessage message :: Session WorkDoneProgressEndNotification)
61+ void (skipManyTill anyMessage message :: Session WorkDoneProgressEndNotification)
57− , initializeResponseTests
62+ , initializeResponseTests
58− , completionTests
63+ , completionTests
59− , cppTests
64+ , cppTests
60− , diagnosticTests
65+ , diagnosticTests
61− , codeActionTests
66+ , codeActionTests
62− , codeLensesTests
67+ , codeLensesTests
63− , outlineTests
68+ , outlineTests
64− , findDefinitionAndHoverTests
69+ , findDefinitionAndHoverTests
65− , pluginTests
70+ , pluginTests
66− , preprocessorTests
71+ , preprocessorTests
67− , thTests
72+ , thTests
68− , safeTests
73+ , safeTests
69− , unitTests
74+ , unitTests
70− , haddockTests
75+ , haddockTests
71− , positionMappingTests
76+ , positionMappingTests
72− , watchedFilesTests
77+ , watchedFilesTests
73− , cradleTests
78+ , cradleTests
76initializeResponseTests :: TestTree
82initializeResponseTests :: TestTree
77initializeResponseTests = withResource acquire release tests where
83initializeResponseTests = withResource acquire release tests where
@@ -106,7 +112,7 @@ initializeResponseTests = withResource acquire release tests where
106 , chk "NO doc link" _documentLinkProvider Nothing
112 , chk "NO doc link" _documentLinkProvider Nothing
107 , chk "NO color" _colorProvider (Just $ ColorOptionsStatic False)
113 , chk "NO color" _colorProvider (Just $ ColorOptionsStatic False)
108 , chk "NO folding range" _foldingRangeProvider (Just $ FoldingRangeOptionsStatic False)
114 , chk "NO folding range" _foldingRangeProvider (Just $ FoldingRangeOptionsStatic False)
109− , chk " execute command" _executeCommandProvider (Just $ ExecuteCommandOptions $ List ["typesignature.add"])
115+ , che " execute command" _executeCommandProvider (Just $ ExecuteCommandOptions $ List ["typesignature.add"])
110 , chk " workspace" _workspace (Just $ WorkspaceOptions (Just WorkspaceFolderOptions{_supported = Just True, _changeNotifications = Just ( WorkspaceFolderChangeNotificationsBool True )}))
116 , chk " workspace" _workspace (Just $ WorkspaceOptions (Just WorkspaceFolderOptions{_supported = Just True, _changeNotifications = Just ( WorkspaceFolderChangeNotificationsBool True )}))
111 , chk "NO experimental" _experimental Nothing
117 , chk "NO experimental" _experimental Nothing
@@ -122,8 +128,17 @@ initializeResponseTests = withResource acquire release tests where
122 chk title getActual expected =
128 chk title getActual expected =
123 testCase title $ getInitializeResponse >>= \ir -> expected @=? (getActual . innerCaps) ir
129 testCase title $ getInitializeResponse >>= \ir -> expected @=? (getActual . innerCaps) ir
131+ che :: TestName -> (InitializeResponseCapabilitiesInner -> Maybe ExecuteCommandOptions) -> Maybe ExecuteCommandOptions -> TestTree
132+ che title getActual _expected = testCase title doTest
135+ ir <- getInitializeResponse
136+ let Just ExecuteCommandOptions {_commands = List [command]} = getActual $ innerCaps ir
137+ True @=? T.isSuffixOf "typesignature.add" command
125 innerCaps :: InitializeResponse -> InitializeResponseCapabilitiesInner
140 innerCaps :: InitializeResponse -> InitializeResponseCapabilitiesInner
126− innerCaps (ResponseMessage _ _ (Just (InitializeResponseCapabilities c)) _) = c
141+ innerCaps (ResponseMessage _ _ (Right (InitializeResponseCapabilities c))) = c
127 innerCaps _ = error "this test only expects inner capabilities"
142 innerCaps _ = error "this test only expects inner capabilities"
129 acquire :: IO InitializeResponse
144 acquire :: IO InitializeResponse
@@ -137,7 +152,7 @@ diagnosticTests :: TestTree
137diagnosticTests = testGroup "diagnostics"
152diagnosticTests = testGroup "diagnostics"
138 [ testSessionWait "fix syntax error" $ do
153 [ testSessionWait "fix syntax error" $ do
139 let content = T.unlines [ "module Testing wher" ]
154 let content = T.unlines [ "module Testing wher" ]
140− doc <- openDoc' "Testing.hs" "haskell" content
155+ doc <- createDoc "Testing.hs" "haskell" content
141 expectDiagnostics [("Testing.hs", [(DsError, (0, 15), "parse error")])]
156 expectDiagnostics [("Testing.hs", [(DsError, (0, 15), "parse error")])]
142 let change = TextDocumentContentChangeEvent
157 let change = TextDocumentContentChangeEvent
143 { _range = Just (Range (Position 0 15) (Position 0 19))
158 { _range = Just (Range (Position 0 15) (Position 0 19))
@@ -148,7 +163,7 @@ diagnosticTests = testGroup "diagnostics"
148 expectDiagnostics [("Testing.hs", [])]
163 expectDiagnostics [("Testing.hs", [])]
149 , testSessionWait "introduce syntax error" $ do
164 , testSessionWait "introduce syntax error" $ do
150 let content = T.unlines [ "module Testing where" ]
165 let content = T.unlines [ "module Testing where" ]
151− doc <- openDoc' "Testing.hs" "haskell" content
166+ doc <- createDoc "Testing.hs" "haskell" content
152 void $ skipManyTill anyMessage (message :: Session WorkDoneProgressCreateRequest)
167 void $ skipManyTill anyMessage (message :: Session WorkDoneProgressCreateRequest)
153 void $ skipManyTill anyMessage (message :: Session WorkDoneProgressBeginNotification)
168 void $ skipManyTill anyMessage (message :: Session WorkDoneProgressBeginNotification)
154 let change = TextDocumentContentChangeEvent
169 let change = TextDocumentContentChangeEvent
@@ -166,7 +181,7 @@ diagnosticTests = testGroup "diagnostics"
166 , "bar :: Int -> Int -> Int"
181 , "bar :: Int -> Int -> Int"
167 , "bar a b = cd + b"
182 , "bar a b = cd + b"
169− _ <- openDoc' "Testing.hs" "haskell" content
184+ _ <- createDoc "Testing.hs" "haskell" content
170 expectDiagnostics
185 expectDiagnostics
171 [ ( "Testing.hs"
186 [ ( "Testing.hs"
172 , [ (DsError, (2, 14), "Variable not in scope: ab")
187 , [ (DsError, (2, 14), "Variable not in scope: ab")
@@ -180,7 +195,7 @@ diagnosticTests = testGroup "diagnostics"
180 , "foo :: Int -> String -> Int"
195 , "foo :: Int -> String -> Int"
181 , "foo a b = a + b"
196 , "foo a b = a + b"
183− _ <- openDoc' "Testing.hs" "haskell" content
198+ _ <- createDoc "Testing.hs" "haskell" content
184 expectDiagnostics
199 expectDiagnostics
185 [ ( "Testing.hs"
200 [ ( "Testing.hs"
186 , [(DsError, (2, 14), "Couldn't match type '[Char]' with 'Int'")]
201 , [(DsError, (2, 14), "Couldn't match type '[Char]' with 'Int'")]
@@ -192,7 +207,7 @@ diagnosticTests = testGroup "diagnostics"
192 , "foo :: Int -> String"
207 , "foo :: Int -> String"
193 , "foo a = _ a"
208 , "foo a = _ a"
195− _ <- openDoc' "Testing.hs" "haskell" content
210+ _ <- createDoc "Testing.hs" "haskell" content
196 expectDiagnostics
211 expectDiagnostics
197 [ ( "Testing.hs"
212 [ ( "Testing.hs"
198 , [(DsError, (2, 8), "Found hole: _ :: Int -> String")]
213 , [(DsError, (2, 8), "Found hole: _ :: Int -> String")]
@@ -214,24 +229,23 @@ diagnosticTests = testGroup "diagnostics"
214 [ ("A.hs", [(DsError, (2,4), aMessage)])
229 [ ("A.hs", [(DsError, (2,4), aMessage)])
215 , ("B.hs", [(DsError, (3,4), bMessage)])]
230 , ("B.hs", [(DsError, (3,4), bMessage)])]
216 deferralTest title binding msg = testSessionWait title $ do
231 deferralTest title binding msg = testSessionWait title $ do
217− _ <- openDoc' "A.hs" "haskell" $ sourceA binding
232+ _ <- createDoc "A.hs" "haskell" $ sourceA binding
218− _ <- openDoc' "B.hs" "haskell" sourceB
233+ _ <- createDoc "B.hs" "haskell" sourceB
219 expectDiagnostics $ expectedDs msg
234 expectDiagnostics $ expectedDs msg
221 [ deferralTest "type error" "True" "Couldn't match expected type"
236 [ deferralTest "type error" "True" "Couldn't match expected type"
222 , deferralTest "typed hole" "_" "Found hole"
237 , deferralTest "typed hole" "_" "Found hole"
223 , deferralTest "out of scope var" "unbound" "Variable not in scope"
238 , deferralTest "out of scope var" "unbound" "Variable not in scope"
224− , deferralTest "message shows error" "True" "A.hs:3:5: error:"
227 , testSessionWait "remove required module" $ do
241 , testSessionWait "remove required module" $ do
228 let contentA = T.unlines [ "module ModuleA where" ]
242 let contentA = T.unlines [ "module ModuleA where" ]
229− docA <- openDoc' "ModuleA.hs" "haskell" contentA
243+ docA <- createDoc "ModuleA.hs" "haskell" contentA
230 let contentB = T.unlines
244 let contentB = T.unlines
231 [ "module ModuleB where"
245 [ "module ModuleB where"
232 , "import ModuleA"
246 , "import ModuleA"
234− _ <- openDoc' "ModuleB.hs" "haskell" contentB
248+ _ <- createDoc "ModuleB.hs" "haskell" contentB
235 let change = TextDocumentContentChangeEvent
249 let change = TextDocumentContentChangeEvent
236 { _range = Just (Range (Position 0 0) (Position 0 20))
250 { _range = Just (Range (Position 0 0) (Position 0 20))
237 , _rangeLength = Nothing
251 , _rangeLength = Nothing
@@ -244,20 +258,20 @@ diagnosticTests = testGroup "diagnostics"
244 [ "module ModuleB where"
258 [ "module ModuleB where"
245 , "import ModuleA"
259 , "import ModuleA"
247− _ <- openDoc' "ModuleB.hs" "haskell" contentB
261+ _ <- createDoc "ModuleB.hs" "haskell" contentB
248 expectDiagnostics [("ModuleB.hs", [(DsError, (1, 7), "Could not find module")])]
262 expectDiagnostics [("ModuleB.hs", [(DsError, (1, 7), "Could not find module")])]
249 let contentA = T.unlines [ "module ModuleA where" ]
263 let contentA = T.unlines [ "module ModuleA where" ]
250− _ <- openDoc' "ModuleA.hs" "haskell" contentA
264+ _ <- createDoc "ModuleA.hs" "haskell" contentA
251 expectDiagnostics [("ModuleB.hs", [])]
265 expectDiagnostics [("ModuleB.hs", [])]
252 , testSessionWait "add missing module (non workspace)" $ do
266 , testSessionWait "add missing module (non workspace)" $ do
253 let contentB = T.unlines
267 let contentB = T.unlines
254 [ "module ModuleB where"
268 [ "module ModuleB where"
255 , "import ModuleA"
269 , "import ModuleA"
257− _ <- openDoc'' "/tmp/ModuleB.hs" "haskell" contentB
271+ _ <- createDoc "/tmp/ModuleB.hs" "haskell" contentB
258 expectDiagnostics [("/tmp/ModuleB.hs", [(DsError, (1, 7), "Could not find module")])]
272 expectDiagnostics [("/tmp/ModuleB.hs", [(DsError, (1, 7), "Could not find module")])]
259 let contentA = T.unlines [ "module ModuleA where" ]
273 let contentA = T.unlines [ "module ModuleA where" ]
260− _ <- openDoc'' "/tmp/ModuleA.hs" "haskell" contentA
274+ _ <- createDoc "/tmp/ModuleA.hs" "haskell" contentA
261 expectDiagnostics [("/tmp/ModuleB.hs", [])]
275 expectDiagnostics [("/tmp/ModuleB.hs", [])]
262 , testSessionWait "cyclic module dependency" $ do
276 , testSessionWait "cyclic module dependency" $ do
263 let contentA = T.unlines
277 let contentA = T.unlines
@@ -268,8 +282,8 @@ diagnosticTests = testGroup "diagnostics"
268 [ "module ModuleB where"
282 [ "module ModuleB where"
269 , "import ModuleA"
283 , "import ModuleA"
271− _ <- openDoc' "ModuleA.hs" "haskell" contentA
285+ _ <- createDoc "ModuleA.hs" "haskell" contentA
272− _ <- openDoc' "ModuleB.hs" "haskell" contentB
286+ _ <- createDoc "ModuleB.hs" "haskell" contentB
273 expectDiagnostics
287 expectDiagnostics
274 [ ( "ModuleA.hs"
288 [ ( "ModuleA.hs"
275 , [(DsError, (1, 7), "Cyclic module dependency between ModuleA, ModuleB")]
289 , [(DsError, (1, 7), "Cyclic module dependency between ModuleA, ModuleB")]
@@ -290,9 +304,9 @@ diagnosticTests = testGroup "diagnostics"
290 let contentBboot = T.unlines
304 let contentBboot = T.unlines
291 [ "module ModuleB where"
305 [ "module ModuleB where"
293− _ <- openDoc' "ModuleA.hs" "haskell" contentA
307+ _ <- createDoc "ModuleA.hs" "haskell" contentA
294− _ <- openDoc' "ModuleB.hs" "haskell" contentB
308+ _ <- createDoc "ModuleB.hs" "haskell" contentB
295− _ <- openDoc' "ModuleB.hs-boot" "haskell" contentBboot
309+ _ <- createDoc "ModuleB.hs-boot" "haskell" contentBboot
296 expectDiagnostics []
310 expectDiagnostics []
297 , testSessionWait "correct reference used with hs-boot" $ do
311 , testSessionWait "correct reference used with hs-boot" $ do
298 let contentB = T.unlines
312 let contentB = T.unlines
@@ -314,10 +328,10 @@ diagnosticTests = testGroup "diagnostics"
314 -- resolved to the hs-boot file
328 -- resolved to the hs-boot file
315 , "y = x"
329 , "y = x"
317− _ <- openDoc' "ModuleB.hs" "haskell" contentB
331+ _ <- createDoc "ModuleB.hs" "haskell" contentB
318− _ <- openDoc' "ModuleA.hs" "haskell" contentA
332+ _ <- createDoc "ModuleA.hs" "haskell" contentA
319− _ <- openDoc' "ModuleA.hs-boot" "haskell" contentAboot
333+ _ <- createDoc "ModuleA.hs-boot" "haskell" contentAboot
320− _ <- openDoc' "ModuleC.hs" "haskell" contentC
334+ _ <- createDoc "ModuleC.hs" "haskell" contentC
321 expectDiagnostics []
335 expectDiagnostics []
322 , testSessionWait "redundant import" $ do
336 , testSessionWait "redundant import" $ do
323 let contentA = T.unlines ["module ModuleA where"]
337 let contentA = T.unlines ["module ModuleA where"]
@@ -326,8 +340,8 @@ diagnosticTests = testGroup "diagnostics"
326 , "module ModuleB where"
340 , "module ModuleB where"
327 , "import ModuleA"
341 , "import ModuleA"
329− _ <- openDoc' "ModuleA.hs" "haskell" contentA
343+ _ <- createDoc "ModuleA.hs" "haskell" contentA
330− _ <- openDoc' "ModuleB.hs" "haskell" contentB
344+ _ <- createDoc "ModuleB.hs" "haskell" contentB
331 expectDiagnostics
345 expectDiagnostics
332 [ ( "ModuleB.hs"
346 [ ( "ModuleB.hs"
333 , [(DsWarning, (2, 0), "The import of 'ModuleA' is redundant")]
347 , [(DsWarning, (2, 0), "The import of 'ModuleA' is redundant")]
@@ -349,8 +363,8 @@ diagnosticTests = testGroup "diagnostics"
349 , "wrong1 = ThisList.map"
363 , "wrong1 = ThisList.map"
350 , "wrong2 = BaseList.x"
364 , "wrong2 = BaseList.x"
352− _ <- openDoc' "Data/List.hs" "haskell" thisDataListContent
366+ _ <- createDoc "Data/List.hs" "haskell" thisDataListContent
353− _ <- openDoc' "Main.hs" "haskell" mainContent
367+ _ <- createDoc "Main.hs" "haskell" mainContent
354 expectDiagnostics
368 expectDiagnostics
355 [ ( "Main.hs"
369 [ ( "Main.hs"
356 , [(DsError, (6, 9), "Not in scope: \8216ThisList.map\8217")
370 , [(DsError, (6, 9), "Not in scope: \8216ThisList.map\8217")
@@ -365,7 +379,7 @@ diagnosticTests = testGroup "diagnostics"
365 , "foo :: Ord a => a -> Int"
379 , "foo :: Ord a => a -> Int"
366 , "foo a = 1"
380 , "foo a = 1"
368− _ <- openDoc' "Foo.hs" "haskell" fooContent
382+ _ <- createDoc "Foo.hs" "haskell" fooContent
369 expectDiagnostics
383 expectDiagnostics
370 [ ( "Foo.hs"
384 [ ( "Foo.hs"
371 -- The test is to make sure that warnings contain unqualified names
385 -- The test is to make sure that warnings contain unqualified names
@@ -390,14 +404,14 @@ diagnosticTests = testGroup "diagnostics"
390 Just pathB <- pure $ uriToFilePath uriB
404 Just pathB <- pure $ uriToFilePath uriB
391 uriB <- pure $
405 uriB <- pure $
392 let (drive, suffix) = splitDrive pathB
406 let (drive, suffix) = splitDrive pathB
393− in filePathToUri (joinDrive (map toLower drive ) suffix)
407+ in filePathToUri (joinDrive (lower drive) suffix)
394 liftIO $ createDirectoryIfMissing True (takeDirectory pathB)
408 liftIO $ createDirectoryIfMissing True (takeDirectory pathB)
395 liftIO $ writeFileUTF8 pathB $ T.unpack bContent
409 liftIO $ writeFileUTF8 pathB $ T.unpack bContent
396 uriA <- getDocUri "A/A.hs"
410 uriA <- getDocUri "A/A.hs"
397 Just pathA <- pure $ uriToFilePath uriA
411 Just pathA <- pure $ uriToFilePath uriA
398 uriA <- pure $
412 uriA <- pure $
399 let (drive, suffix) = splitDrive pathA
413 let (drive, suffix) = splitDrive pathA
400− in filePathToUri (joinDrive (map toLower drive ) suffix)
414+ in filePathToUri (joinDrive (lower drive) suffix)
401 let itemA = TextDocumentItem uriA "haskell" 0 aContent
415 let itemA = TextDocumentItem uriA "haskell" 0 aContent
402 let a = TextDocumentIdentifier uriA
416 let a = TextDocumentIdentifier uriA
403 sendNotification TextDocumentDidOpen (DidOpenTextDocumentParams itemA)
417 sendNotification TextDocumentDidOpen (DidOpenTextDocumentParams itemA)
@@ -410,6 +424,38 @@ diagnosticTests = testGroup "diagnostics"
410 liftIO $ unless ("redundant" `T.isInfixOf` msg) $
424 liftIO $ unless ("redundant" `T.isInfixOf` msg) $
411 assertFailure ("Expected redundant import but got " <> T.unpack msg)
425 assertFailure ("Expected redundant import but got " <> T.unpack msg)
412 closeDoc a
426 closeDoc a
427+ , testSessionWait "haddock parse error" $ do
428+ let fooContent = T.unlines
429+ [ "module Foo where"
433+ _ <- createDoc "Foo.hs" "haskell" fooContent
436+ , [(DsError, (2, 8), "Parse error on input")
440+ , testSessionWait "strip file path" $ do
444+ [ "module " <> name <> " where"
445+ , "value :: Maybe ()"
448+ _ <- createDoc (T.unpack name <> ".hs") "haskell" content
449+ notification <- skipManyTill anyMessage diagnostic
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
415codeActionTests :: TestTree
461codeActionTests :: TestTree
@@ -435,26 +481,26 @@ codeLensesTests = testGroup "code lenses"
435watchedFilesTests :: TestTree
481watchedFilesTests :: TestTree
436watchedFilesTests = testGroup "watched files"
482watchedFilesTests = testGroup "watched files"
437 [ testSession' "workspace files" $ \sessionDir -> do
483 [ testSession' "workspace files" $ \sessionDir -> do
438− liftIO $ writeFile (sessionDir </> "hie.yaml") $ "cradle: {direct: {arguments: [\"-isrc\"]}}"
484+ liftIO $ writeFile (sessionDir </> "hie.yaml") "cradle: {direct: {arguments: [\"-isrc\"]}}"
439− _ <- openDoc' "A.hs" "haskell" "{-#LANGUAGE NoImplicitPrelude #-}\nmodule A where\nimport B"
485+ _doc <- createDoc "A.hs" "haskell" "{-#LANGUAGE NoImplicitPrelude #-}\nmodule A where\nimport WatchedFilesMissingModule"
440− watchedFileRegs <- getWatchedFilesSubscriptionsUntilProgressEnd
486+ watchedFileRegs <- getWatchedFilesSubscriptionsUntil @PublishDiagnosticsNotification
442− -- Expect 6 subscriptions (A does not get any because it's VFS):
488+ -- Expect 4 subscriptions (A does not get any because it's VFS):
443− -- - /path-to-workspace/B.hs
489+ -- - /path-to-workspace/WatchedFilesMissingModule.hs
444− -- - /path-to-workspace/B.lhs
490+ -- - /path-to-workspace/WatchedFilesMissingModule.lhs
445− -- - B.hs
491+ -- - /path-to-workspace/src/WatchedFilesMissingModule.hs
446− -- - B.lhs
492+ -- - /path-to-workspace/src/WatchedFilesMissingModule.lhs
449− liftIO $ length watchedFileRegs @?= 6
493+ liftIO $ length watchedFileRegs @?= 4
451 , testSession' "non workspace file" $ \sessionDir -> do
495 , testSession' "non workspace file" $ \sessionDir -> do
452− liftIO $ writeFile (sessionDir </> "hie.yaml") $ "cradle: {direct: {arguments: [\"-i/tmp\"]}}"
496+ liftIO $ writeFile (sessionDir </> "hie.yaml") "cradle: {direct: {arguments: [\"-i/tmp\"]}}"
453− _ <- openDoc' "A.hs" "haskell" "{-# LANGUAGE NoImplicitPrelude#-}\nmodule A where\nimport B"
497+ _doc <- createDoc "A.hs" "haskell" "{-# LANGUAGE NoImplicitPrelude#-}\nmodule A where\nimport WatchedFilesMissingModule"
454− watchedFileRegs <- getWatchedFilesSubscriptionsUntilProgressEnd
498+ watchedFileRegs <- getWatchedFilesSubscriptionsUntil @PublishDiagnosticsNotification
456− -- Expect 4 subscriptions:
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
457− liftIO $ length watchedFileRegs @?= 4
503+ liftIO $ length watchedFileRegs @?= 2
459 -- TODO add a test for didChangeWorkspaceFolder
505 -- TODO add a test for didChangeWorkspaceFolder
@@ -467,7 +513,7 @@ renameActionTests = testGroup "rename actions"
467 , "foo :: Int -> Int"
513 , "foo :: Int -> Int"
468 , "foo argName = argNme"
514 , "foo argName = argNme"
470− doc <- openDoc' "Testing.hs" "haskell" content
516+ doc <- createDoc "Testing.hs" "haskell" content
471 _ <- waitForDiagnostics
517 _ <- waitForDiagnostics
472 action <- findCodeAction doc (Range (Position 2 14) (Position 2 20)) "Replace with ‘argName’"
518 action <- findCodeAction doc (Range (Position 2 14) (Position 2 20)) "Replace with ‘argName’"
473 executeCodeAction action
519 executeCodeAction action
@@ -485,7 +531,7 @@ renameActionTests = testGroup "rename actions"
485 , "foo :: Maybe a -> [a]"
531 , "foo :: Maybe a -> [a]"
486 , "foo = maybToList"
532 , "foo = maybToList"
488− doc <- openDoc' "Testing.hs" "haskell" content
534+ doc <- createDoc "Testing.hs" "haskell" content
489 _ <- waitForDiagnostics
535 _ <- waitForDiagnostics
490 action <- findCodeAction doc (Range (Position 3 6) (Position 3 16)) "Replace with ‘maybeToList’"
536 action <- findCodeAction doc (Range (Position 3 6) (Position 3 16)) "Replace with ‘maybeToList’"
491 executeCodeAction action
537 executeCodeAction action
@@ -503,7 +549,7 @@ renameActionTests = testGroup "rename actions"
503 , "foo :: Char -> Char -> Char -> Char"
549 , "foo :: Char -> Char -> Char -> Char"
504 , "foo argument1 argument2 argument3 = argumentX"
550 , "foo argument1 argument2 argument3 = argumentX"
506− doc <- openDoc' "Testing.hs" "haskell" content
552+ doc <- createDoc "Testing.hs" "haskell" content
507 _ <- waitForDiagnostics
553 _ <- waitForDiagnostics
508 _ <- findCodeActions doc (Range (Position 2 36) (Position 2 45))
554 _ <- findCodeActions doc (Range (Position 2 36) (Position 2 45))
509 ["Replace with ‘argument1’", "Replace with ‘argument2’", "Replace with ‘argument3’"]
555 ["Replace with ‘argument1’", "Replace with ‘argument2’", "Replace with ‘argument3’"]
@@ -515,7 +561,7 @@ renameActionTests = testGroup "rename actions"
515 , "monus x y = max 0 (x - y)"
561 , "monus x y = max 0 (x - y)"
516 , "foo x y = x `monnus` y"
562 , "foo x y = x `monnus` y"
518− doc <- openDoc' "Testing.hs" "haskell" content
564+ doc <- createDoc "Testing.hs" "haskell" content
519 _ <- waitForDiagnostics
565 _ <- waitForDiagnostics
520 actionsOrCommands <- getCodeActions doc (Range (Position 3 12) (Position 3 20))
566 actionsOrCommands <- getCodeActions doc (Range (Position 3 12) (Position 3 20))
521 [fixTypo] <- pure [action | CACodeAction action@CodeAction{ _title = actionTitle } <- actionsOrCommands, "monus" `T.isInfixOf` actionTitle ]
567 [fixTypo] <- pure [action | CACodeAction action@CodeAction{ _title = actionTitle } <- actionsOrCommands, "monus" `T.isInfixOf` actionTitle ]
@@ -538,7 +584,7 @@ typeWildCardActionTests = testGroup "type wildcard actions"
538 , "func :: _"
584 , "func :: _"
539 , "func x = x"
585 , "func x = x"
541− doc <- openDoc' "Testing.hs" "haskell" content
587+ doc <- createDoc "Testing.hs" "haskell" content
542 _ <- waitForDiagnostics
588 _ <- waitForDiagnostics
543 actionsOrCommands <- getCodeActions doc (Range (Position 2 1) (Position 2 10))
589 actionsOrCommands <- getCodeActions doc (Range (Position 2 1) (Position 2 10))
544 let [addSignature] = [action | CACodeAction action@CodeAction { _title = actionTitle } <- actionsOrCommands
590 let [addSignature] = [action | CACodeAction action@CodeAction { _title = actionTitle } <- actionsOrCommands
@@ -558,7 +604,7 @@ typeWildCardActionTests = testGroup "type wildcard actions"
558 , "func :: _"
604 , "func :: _"
559 , "func x y = x + y"
605 , "func x y = x + y"
561− doc <- openDoc' "Testing.hs" "haskell" content
607+ doc <- createDoc "Testing.hs" "haskell" content
562 _ <- waitForDiagnostics
608 _ <- waitForDiagnostics
563 actionsOrCommands <- getCodeActions doc (Range (Position 2 1) (Position 2 10))
609 actionsOrCommands <- getCodeActions doc (Range (Position 2 1) (Position 2 10))
564 let [addSignature] = [action | CACodeAction action@CodeAction { _title = actionTitle } <- actionsOrCommands
610 let [addSignature] = [action | CACodeAction action@CodeAction { _title = actionTitle } <- actionsOrCommands
@@ -581,7 +627,7 @@ typeWildCardActionTests = testGroup "type wildcard actions"
581 , " y = x * 2"
627 , " y = x * 2"
582 , " in y"
628 , " in y"
584− doc <- openDoc' "Testing.hs" "haskell" content
630+ doc <- createDoc "Testing.hs" "haskell" content
585 _ <- waitForDiagnostics
631 _ <- waitForDiagnostics
586 actionsOrCommands <- getCodeActions doc (Range (Position 4 1) (Position 4 10))
632 actionsOrCommands <- getCodeActions doc (Range (Position 4 1) (Position 4 10))
587 let [addSignature] = [action | CACodeAction action@CodeAction { _title = actionTitle } <- actionsOrCommands
633 let [addSignature] = [action | CACodeAction action@CodeAction { _title = actionTitle } <- actionsOrCommands
@@ -606,7 +652,7 @@ removeImportTests = testGroup "remove import actions"
606 let contentA = T.unlines
652 let contentA = T.unlines
607 [ "module ModuleA where"
653 [ "module ModuleA where"
609− _docA <- openDoc' "ModuleA.hs" "haskell" contentA
655+ _docA <- createDoc "ModuleA.hs" "haskell" contentA
610 let contentB = T.unlines
656 let contentB = T.unlines
611 [ "{-# OPTIONS_GHC -Wunused-imports #-}"
657 [ "{-# OPTIONS_GHC -Wunused-imports #-}"
612 , "module ModuleB where"
658 , "module ModuleB where"
@@ -614,7 +660,7 @@ removeImportTests = testGroup "remove import actions"
614 , "stuffB :: Integer"
660 , "stuffB :: Integer"
615 , "stuffB = 123"
661 , "stuffB = 123"
617− docB <- openDoc' "ModuleB.hs" "haskell" contentB
663+ docB <- createDoc "ModuleB.hs" "haskell" contentB
618 _ <- waitForDiagnostics
664 _ <- waitForDiagnostics
619 [CACodeAction action@CodeAction { _title = actionTitle }]
665 [CACodeAction action@CodeAction { _title = actionTitle }]
620 <- getCodeActions docB (Range (Position 2 0) (Position 2 5))
666 <- getCodeActions docB (Range (Position 2 0) (Position 2 5))
@@ -632,7 +678,7 @@ removeImportTests = testGroup "remove import actions"
632 let contentA = T.unlines
678 let contentA = T.unlines
633 [ "module ModuleA where"
679 [ "module ModuleA where"
635− _docA <- openDoc' "ModuleA.hs" "haskell" contentA
681+ _docA <- createDoc "ModuleA.hs" "haskell" contentA
636 let contentB = T.unlines
682 let contentB = T.unlines
637 [ "{-# OPTIONS_GHC -Wunused-imports #-}"
683 [ "{-# OPTIONS_GHC -Wunused-imports #-}"
638 , "module ModuleB where"
684 , "module ModuleB where"
@@ -640,7 +686,7 @@ removeImportTests = testGroup "remove import actions"
640 , "stuffB :: Integer"
686 , "stuffB :: Integer"
641 , "stuffB = 123"
687 , "stuffB = 123"
643− docB <- openDoc' "ModuleB.hs" "haskell" contentB
689+ docB <- createDoc "ModuleB.hs" "haskell" contentB
644 _ <- waitForDiagnostics
690 _ <- waitForDiagnostics
645 [CACodeAction action@CodeAction { _title = actionTitle }]
691 [CACodeAction action@CodeAction { _title = actionTitle }]
646 <- getCodeActions docB (Range (Position 2 0) (Position 2 5))
692 <- getCodeActions docB (Range (Position 2 0) (Position 2 5))
@@ -662,14 +708,14 @@ removeImportTests = testGroup "remove import actions"
662 , "stuffB = 123"
708 , "stuffB = 123"
663 , "stuffC = ()"
709 , "stuffC = ()"
665− _docA <- openDoc' "ModuleA.hs" "haskell" contentA
711+ _docA <- createDoc "ModuleA.hs" "haskell" contentA
666 let contentB = T.unlines
712 let contentB = T.unlines
667 [ "{-# OPTIONS_GHC -Wunused-imports #-}"
713 [ "{-# OPTIONS_GHC -Wunused-imports #-}"
668 , "module ModuleB where"
714 , "module ModuleB where"
669 , "import ModuleA (stuffA, stuffB, stuffC, stuffA)"
715 , "import ModuleA (stuffA, stuffB, stuffC, stuffA)"
670 , "main = print stuffB"
716 , "main = print stuffB"
672− docB <- openDoc' "ModuleB.hs" "haskell" contentB
718+ docB <- createDoc "ModuleB.hs" "haskell" contentB
673 _ <- waitForDiagnostics
719 _ <- waitForDiagnostics
674 [CACodeAction action@CodeAction { _title = actionTitle }]
720 [CACodeAction action@CodeAction { _title = actionTitle }]
675 <- getCodeActions docB (Range (Position 2 0) (Position 2 5))
721 <- getCodeActions docB (Range (Position 2 0) (Position 2 5))
@@ -691,22 +737,18 @@ removeImportTests = testGroup "remove import actions"
691 , "stuffB :: Integer"
737 , "stuffB :: Integer"
692 , "stuffB = 123"
738 , "stuffB = 123"
694− _docA <- openDoc' "ModuleA.hs" "haskell" contentA
740+ _docA <- createDoc "ModuleA.hs" "haskell" contentA
695 let contentB = T.unlines
741 let contentB = T.unlines
696 [ "{-# OPTIONS_GHC -Wunused-imports #-}"
742 [ "{-# OPTIONS_GHC -Wunused-imports #-}"
697 , "module ModuleB where"
743 , "module ModuleB where"
698 , "import qualified ModuleA as A ((<?>), stuffB, (!!))"
744 , "import qualified ModuleA as A ((<?>), stuffB, (!!))"
699 , "main = print A.stuffB"
745 , "main = print A.stuffB"
701− docB <- openDoc' "ModuleB.hs" "haskell" contentB
747+ docB <- createDoc "ModuleB.hs" "haskell" contentB
702 _ <- waitForDiagnostics
748 _ <- waitForDiagnostics
703 [CACodeAction action@CodeAction { _title = actionTitle }]
749 [CACodeAction action@CodeAction { _title = actionTitle }]
704 <- getCodeActions docB (Range (Position 2 0) (Position 2 5))
750 <- getCodeActions docB (Range (Position 2 0) (Position 2 5))
705−#if MIN_GHC_API_VERSION(8,6,0)
706 liftIO $ "Remove !!, <?> from import" @=? actionTitle
751 liftIO $ "Remove !!, <?> from import" @=? actionTitle
708− liftIO $ "Remove A.!!, A.<?> from import" @=? actionTitle
710 executeCodeAction action
752 executeCodeAction action
711 contentAfterAction <- documentContents docB
753 contentAfterAction <- documentContents docB
712 let expectedContentAfterAction = T.unlines
754 let expectedContentAfterAction = T.unlines
@@ -723,14 +765,14 @@ removeImportTests = testGroup "remove import actions"
723 , "stuffB :: Integer"
765 , "stuffB :: Integer"
724 , "stuffB = 123"
766 , "stuffB = 123"
726− _docA <- openDoc' "ModuleA.hs" "haskell" contentA
768+ _docA <- createDoc "ModuleA.hs" "haskell" contentA
727 let contentB = T.unlines
769 let contentB = T.unlines
728 [ "{-# OPTIONS_GHC -Wunused-imports #-}"
770 [ "{-# OPTIONS_GHC -Wunused-imports #-}"
729 , "module ModuleB where"
771 , "module ModuleB where"
730 , "import ModuleA (A(..), stuffB)"
772 , "import ModuleA (A(..), stuffB)"
731 , "main = print stuffB"
773 , "main = print stuffB"
733− docB <- openDoc' "ModuleB.hs" "haskell" contentB
775+ docB <- createDoc "ModuleB.hs" "haskell" contentB
734 _ <- waitForDiagnostics
776 _ <- waitForDiagnostics
735 [CACodeAction action@CodeAction { _title = actionTitle }]
777 [CACodeAction action@CodeAction { _title = actionTitle }]
736 <- getCodeActions docB (Range (Position 2 0) (Position 2 5))
778 <- getCodeActions docB (Range (Position 2 0) (Position 2 5))
@@ -750,14 +792,14 @@ removeImportTests = testGroup "remove import actions"
750 , "data D = A | B"
792 , "data D = A | B"
751 , "data E = F"
793 , "data E = F"
753− _docA <- openDoc' "ModuleA.hs" "haskell" contentA
795+ _docA <- createDoc "ModuleA.hs" "haskell" contentA
754 let contentB = T.unlines
796 let contentB = T.unlines
755 [ "{-# OPTIONS_GHC -Wunused-imports #-}"
797 [ "{-# OPTIONS_GHC -Wunused-imports #-}"
756 , "module ModuleB where"
798 , "module ModuleB where"
757 , "import ModuleA (D(A,B), E(F))"
799 , "import ModuleA (D(A,B), E(F))"
758 , "main = B"
800 , "main = B"
760− docB <- openDoc' "ModuleB.hs" "haskell" contentB
802+ docB <- createDoc "ModuleB.hs" "haskell" contentB
761 _ <- waitForDiagnostics
803 _ <- waitForDiagnostics
762 [CACodeAction action@CodeAction { _title = actionTitle }]
804 [CACodeAction action@CodeAction { _title = actionTitle }]
763 <- getCodeActions docB (Range (Position 2 0) (Position 2 5))
805 <- getCodeActions docB (Range (Position 2 0) (Position 2 5))
@@ -898,8 +940,8 @@ extendImportTests = testGroup "extend import actions"
900 template contentA contentB range expectedAction expectedContentB = do
942 template contentA contentB range expectedAction expectedContentB = do
901− _docA <- openDoc' "ModuleA.hs" "haskell" contentA
943+ _docA <- createDoc "ModuleA.hs" "haskell" contentA
902− docB <- openDoc' "ModuleB.hs" "haskell" contentB
944+ docB <- createDoc "ModuleB.hs" "haskell" contentB
903 _ <- waitForDiagnostics
945 _ <- waitForDiagnostics
904 CACodeAction action@CodeAction { _title = actionTitle } : _
946 CACodeAction action@CodeAction { _title = actionTitle } : _
905 <- sortOn (\(CACodeAction CodeAction{_title=x}) -> x) <$>
947 <- sortOn (\(CACodeAction CodeAction{_title=x}) -> x) <$>
@@ -939,6 +981,8 @@ suggestImportTests = testGroup "suggest import actions"
939 , test True [] "f :: Typeable a => a" ["f = undefined"] "import Data.Typeable (Typeable)"
981 , test True [] "f :: Typeable a => a" ["f = undefined"] "import Data.Typeable (Typeable)"
940 , test True [] "f = pack" [] "import Data.Text (pack)"
982 , test True [] "f = pack" [] "import Data.Text (pack)"
941 , test True [] "f :: Text" ["f = undefined"] "import Data.Text (Text)"
983 , 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 ((&))"
@@ -947,19 +991,20 @@ suggestImportTests = testGroup "suggest import actions"
947 after = T.unlines $ "module A where" : ["import " <> x | x <- imps] ++ [newImp] ++ def : other
991 after = T.unlines $ "module A where" : ["import " <> x | x <- imps] ++ [newImp] ++ def : other
948 cradle = "cradle: {direct: {arguments: [-hide-all-packages, -package, base, -package, text, -package-env, -]}}"
992 cradle = "cradle: {direct: {arguments: [-hide-all-packages, -package, base, -package, text, -package-env, -]}}"
949 liftIO $ writeFileUTF8 (dir </> "hie.yaml") cradle
993 liftIO $ writeFileUTF8 (dir </> "hie.yaml") cradle
950− doc <- openDoc' "Test.hs" "haskell" before
994+ doc <- createDoc "Test.hs" "haskell" before
951 _diags <- waitForDiagnostics
995 _diags <- waitForDiagnostics
952 let defLine = length imps + 1
996 let defLine = length imps + 1
953 range = Range (Position defLine 0) (Position defLine maxBound)
997 range = Range (Position defLine 0) (Position defLine maxBound)
954 actions <- getCodeActions doc range
998 actions <- getCodeActions doc range
955− case wanted of
999+ if wanted
956− False ->
1000+ then do
1001+ action <- liftIO $ pickActionWithTitle newImp actions
1002+ executeCodeAction action
1003+ contentAfterAction <- documentContents doc
1004+ liftIO $ after @=? contentAfterAction
957− liftIO $ [_title | CACodeAction CodeAction{_title} <- actions, _title == newImp ] @?= []
1006+ liftIO $ [_title | CACodeAction CodeAction{_title} <- actions, _title == newImp ] @?= []
959− action <- liftIO $ pickActionWithTitle newImp actions
960− executeCodeAction action
961− contentAfterAction <- documentContents doc
962− liftIO $ after @=? contentAfterAction
964addExtensionTests :: TestTree
1009addExtensionTests :: TestTree
965addExtensionTests = testGroup "add language extension actions"
1010addExtensionTests = testGroup "add language extension actions"
@@ -1006,7 +1051,7 @@ addExtensionTests = testGroup "add language extension actions"
1008 template initialContent range expectedAction expectedContents = do
1053 template initialContent range expectedAction expectedContents = do
1009− doc <- openDoc' "Module.hs" "haskell" initialContent
1054+ doc <- createDoc "Module.hs" "haskell" initialContent
1010 _ <- waitForDiagnostics
1055 _ <- waitForDiagnostics
1011 CACodeAction action@CodeAction { _title = actionTitle } : _
1056 CACodeAction action@CodeAction { _title = actionTitle } : _
1012 <- sortOn (\(CACodeAction CodeAction{_title=x}) -> x) <$>
1057 <- sortOn (\(CACodeAction CodeAction{_title=x}) -> x) <$>
@@ -1029,7 +1074,7 @@ insertNewDefinitionTests = testGroup "insert new definition actions"
1030 ,"someOtherCode = ()"
1075 ,"someOtherCode = ()"
1032− docB <- openDoc' "ModuleB.hs" "haskell" (T.unlines $ txtB ++ txtB')
1077+ docB <- createDoc "ModuleB.hs" "haskell" (T.unlines $ txtB ++ txtB')
1033 _ <- waitForDiagnostics
1078 _ <- waitForDiagnostics
1034 CACodeAction action@CodeAction { _title = actionTitle } : _
1079 CACodeAction action@CodeAction { _title = actionTitle } : _
1035 <- sortOn (\(CACodeAction CodeAction{_title=x}) -> x) <$>
1080 <- sortOn (\(CACodeAction CodeAction{_title=x}) -> x) <$>
@@ -1053,7 +1098,7 @@ insertNewDefinitionTests = testGroup "insert new definition actions"
1054 ,"someOtherCode = ()"
1099 ,"someOtherCode = ()"
1056− docB <- openDoc' "ModuleB.hs" "haskell" (T.unlines $ txtB ++ txtB')
1101+ docB <- createDoc "ModuleB.hs" "haskell" (T.unlines $ txtB ++ txtB')
1057 _ <- waitForDiagnostics
1102 _ <- waitForDiagnostics
1058 CACodeAction action@CodeAction { _title = actionTitle } : _
1103 CACodeAction action@CodeAction { _title = actionTitle } : _
1059 <- sortOn (\(CACodeAction CodeAction{_title=x}) -> x) <$>
1104 <- sortOn (\(CACodeAction CodeAction{_title=x}) -> x) <$>
@@ -1092,8 +1137,8 @@ fixConstructorImportTests = testGroup "fix import actions"
1094 template contentA contentB range expectedAction expectedContentB = do
1139 template contentA contentB range expectedAction expectedContentB = do
1095− _docA <- openDoc' "ModuleA.hs" "haskell" contentA
1140+ _docA <- createDoc "ModuleA.hs" "haskell" contentA
1096− docB <- openDoc' "ModuleB.hs" "haskell" contentB
1141+ docB <- createDoc "ModuleB.hs" "haskell" contentB
1097 _diags <- waitForDiagnostics
1142 _diags <- waitForDiagnostics
1098 CACodeAction action@CodeAction { _title = actionTitle } : _
1143 CACodeAction action@CodeAction { _title = actionTitle } : _
1099 <- sortOn (\(CACodeAction CodeAction{_title=x}) -> x) <$>
1144 <- sortOn (\(CACodeAction CodeAction{_title=x}) -> x) <$>
@@ -1112,7 +1157,7 @@ importRenameActionTests = testGroup "import rename actions"
1112 [ "module Testing where"
1157 [ "module Testing where"
1113 , "import Data.Mape"
1158 , "import Data.Mape"
1115− doc <- openDoc' "Testing.hs" "haskell" content
1160+ doc <- createDoc "Testing.hs" "haskell" content
1116 _ <- waitForDiagnostics
1161 _ <- waitForDiagnostics
1117 actionsOrCommands <- getCodeActions doc (Range (Position 2 8) (Position 2 16))
1162 actionsOrCommands <- getCodeActions doc (Range (Position 2 8) (Position 2 16))
1118 let [changeToMap] = [action | CACodeAction action@CodeAction{ _title = actionTitle } <- actionsOrCommands, ("Data." <> modname) `T.isInfixOf` actionTitle ]
1163 let [changeToMap] = [action | CACodeAction action@CodeAction{ _title = actionTitle } <- actionsOrCommands, ("Data." <> modname) `T.isInfixOf` actionTitle ]
@@ -1149,7 +1194,7 @@ fillTypedHoleTests = let
1149 newA newB newC = testSession (T.unpack actionTitle) $ do
1194 newA newB newC = testSession (T.unpack actionTitle) $ do
1150 let originalCode = sourceCode oldA oldB oldC
1195 let originalCode = sourceCode oldA oldB oldC
1151 let expectedCode = sourceCode newA newB newC
1196 let expectedCode = sourceCode newA newB newC
1152− doc <- openDoc' "Testing.hs" "haskell" originalCode
1197+ doc <- createDoc "Testing.hs" "haskell" originalCode
1153 _ <- waitForDiagnostics
1198 _ <- waitForDiagnostics
1154 actionsOrCommands <- getCodeActions doc (Range (Position 9 0) (Position 9 maxBound))
1199 actionsOrCommands <- getCodeActions doc (Range (Position 9 0) (Position 9 maxBound))
1155 chosenAction <- liftIO $ pickActionWithTitle actionTitle actionsOrCommands
1200 chosenAction <- liftIO $ pickActionWithTitle actionTitle actionsOrCommands
@@ -1197,7 +1242,7 @@ addSigActionTests = let
1197 def >:: sig = testSession (T.unpack def) $ do
1242 def >:: sig = testSession (T.unpack def) $ do
1198 let originalCode = before def
1243 let originalCode = before def
1199 let expectedCode = after' def sig
1244 let expectedCode = after' def sig
1200− doc <- openDoc' "Sigs.hs" "haskell" originalCode
1245+ doc <- createDoc "Sigs.hs" "haskell" originalCode
1201 _ <- waitForDiagnostics
1246 _ <- waitForDiagnostics
1202 actionsOrCommands <- getCodeActions doc (Range (Position 3 1) (Position 3 maxBound))
1247 actionsOrCommands <- getCodeActions doc (Range (Position 3 1) (Position 3 maxBound))
1203 chosenAction <- liftIO $ pickActionWithTitle ("add signature: " <> sig) actionsOrCommands
1248 chosenAction <- liftIO $ pickActionWithTitle ("add signature: " <> sig) actionsOrCommands
@@ -1229,7 +1274,7 @@ addSigLensesTests = let
1229 sigSession withMissing def sig = testSession (T.unpack def) $ do
1274 sigSession withMissing def sig = testSession (T.unpack def) $ do
1230 let originalCode = before withMissing def
1275 let originalCode = before withMissing def
1231 let expectedCode = after' withMissing def sig
1276 let expectedCode = after' withMissing def sig
1232− doc <- openDoc' "Sigs.hs" "haskell" originalCode
1277+ doc <- createDoc "Sigs.hs" "haskell" originalCode
1233 [CodeLens {_command = Just c}] <- getCodeLenses doc
1278 [CodeLens {_command = Just c}] <- getCodeLenses doc
1234 executeCommand c
1279 executeCommand c
1235 modifiedCode <- getDocumentEdit doc
1280 modifiedCode <- getDocumentEdit doc
@@ -1251,27 +1296,19 @@ addSigLensesTests = let
1254−findDefinitionAndHoverTests :: TestTree
1255−findDefinitionAndHoverTests = let
1257− tst (get, check) pos targetRange title = testSession title $ do
1258− doc <- openTestDataDoc sourceFilePath
1259− found <- get doc pos
1260− check found targetRange
1262− checkDefs :: [Location] -> Session [Expect] -> Session ()
1299+checkDefs :: [Location] -> Session [Expect] -> Session ()
1263− checkDefs defs mkExpectations = traverse_ check =<< mkExpectations where
1300+checkDefs defs mkExpectations = traverse_ check =<< mkExpectations where
1265− check (ExpectRange expectedRange) = do
1302+ check (ExpectRange expectedRange) = do
1266− assertNDefinitionsFound 1 defs
1303+ assertNDefinitionsFound 1 defs
1267− assertRangeCorrect (head defs) expectedRange
1304+ assertRangeCorrect (head defs) expectedRange
1268− check (ExpectLocation expectedLocation) = do
1305+ check (ExpectLocation expectedLocation) = do
1269− assertNDefinitionsFound 1 defs
1306+ assertNDefinitionsFound 1 defs
1270− liftIO $ head defs @?= expectedLocation
1307+ liftIO $ head defs @?= expectedLocation
1271− check ExpectNoDefinitions = do
1308+ check ExpectNoDefinitions = do
1272− assertNDefinitionsFound 0 defs
1309+ assertNDefinitionsFound 0 defs
1273− check ExpectExternFail = liftIO $ assertFailure "Expecting to fail to find in external file"
1310+ check ExpectExternFail = liftIO $ assertFailure "Expecting to fail to find in external file"
1274− check _ = pure () -- all other expectations not relevant to getDefinition
1311+ check _ = pure () -- all other expectations not relevant to getDefinition
1276 assertNDefinitionsFound :: Int -> [a] -> Session ()
1313 assertNDefinitionsFound :: Int -> [a] -> Session ()
1277 assertNDefinitionsFound n defs = liftIO $ assertEqual "number of definitions" n (length defs)
1314 assertNDefinitionsFound n defs = liftIO $ assertEqual "number of definitions" n (length defs)
@@ -1279,6 +1316,17 @@ findDefinitionAndHoverTests = let
1279 assertRangeCorrect Location{_range = foundRange} expectedRange =
1316 assertRangeCorrect Location{_range = foundRange} expectedRange =
1280 liftIO $ expectedRange @=? foundRange
1317 liftIO $ expectedRange @=? foundRange
1320+findDefinitionAndHoverTests :: TestTree
1321+findDefinitionAndHoverTests = let
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
1282 checkHover :: Maybe Hover -> Session [Expect] -> Session ()
1330 checkHover :: Maybe Hover -> Session [Expect] -> Session ()
1283 checkHover hover expectations = traverse_ check =<< expectations where
1331 checkHover hover expectations = traverse_ check =<< expectations where
@@ -1323,7 +1371,8 @@ findDefinitionAndHoverTests = let
1324 mkFindTests tests = testGroup "get"
1372 mkFindTests tests = testGroup "get"
1325 [ testGroup "definition" $ mapMaybe fst tests
1373 [ testGroup "definition" $ mapMaybe fst tests
1326− , testGroup "hover" $ mapMaybe snd tests ]
1374+ , testGroup "hover" $ mapMaybe snd tests
1375+ , checkFileCompiles sourceFilePath ]
1328 test runDef runHover look expect = testM runDef runHover look (return expect)
1377 test runDef runHover look expect = testM runDef runHover look (return expect)
@@ -1418,8 +1467,17 @@ findDefinitionAndHoverTests = let
1418 broken = Just . (`xfail` "known broken")
1467 broken = Just . (`xfail` "known broken")
1419 no = const Nothing -- don't run this test at all
1468 no = const Nothing -- don't run this test at all
1470+checkFileCompiles :: FilePath -> TestTree
1471+checkFileCompiles fp =
1472+ testSessionWithExtraFiles "hover" ("Does " ++ fp ++ " compile") $ \dir -> do
1473+ void (openTestDataDoc (dir </> fp))
1474+ expectNoMoreDiagnostics 0.5
1421pluginTests :: TestTree
1478pluginTests :: TestTree
1479+pluginTests = (`xfail8101` "known broken (#556)")
1422−pluginTests = testSessionWait "plugins" $ do
1480+ $ testSessionWait "plugins" $ do
1423 let content =
1481 let content =
1424 T.unlines
1482 T.unlines
1425 [ "{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}"
1483 [ "{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}"
@@ -1433,7 +1491,7 @@ pluginTests = testSessionWait "plugins" $ do
1433 , "foo :: Int -> Int -> Int"
1491 , "foo :: Int -> Int -> Int"
1434 , "foo a b = a + c"
1492 , "foo a b = a + c"
1436− _ <- openDoc' "Testing.hs" "haskell" content
1494+ _ <- createDoc "Testing.hs" "haskell" content
1437 expectDiagnostics
1495 expectDiagnostics
1438 [ ( "Testing.hs",
1496 [ ( "Testing.hs",
1439 [(DsError, (8, 14), "Variable not in scope: c")]
1497 [(DsError, (8, 14), "Variable not in scope: c")]
@@ -1460,7 +1518,7 @@ cppTests =
1460 run $ expectError content (2, 1)
1518 run $ expectError content (2, 1)
1462 , testSessionWait "cpp-ghcide" $ do
1520 , testSessionWait "cpp-ghcide" $ do
1463− _ <- openDoc' "A.hs" "haskell" $ T.unlines
1521+ _ <- createDoc "A.hs" "haskell" $ T.unlines
1464 ["{-# LANGUAGE CPP #-}"
1522 ["{-# LANGUAGE CPP #-}"
1465 ,"main ="
1523 ,"main ="
1466 ,"#ifdef __GHCIDE__"
1524 ,"#ifdef __GHCIDE__"
@@ -1474,7 +1532,7 @@ cppTests =
1475 expectError :: T.Text -> Cursor -> Session ()
1533 expectError :: T.Text -> Cursor -> Session ()
1476 expectError content cursor = do
1534 expectError content cursor = do
1477− _ <- openDoc' "Testing.hs" "haskell" content
1535+ _ <- createDoc "Testing.hs" "haskell" content
1478 expectDiagnostics
1536 expectDiagnostics
1479 [ ( "Testing.hs",
1537 [ ( "Testing.hs",
1480 [(DsError, cursor, "error: unterminated")]
1538 [(DsError, cursor, "error: unterminated")]
@@ -1490,7 +1548,7 @@ preprocessorTests = testSessionWait "preprocessor" $ do
1490 , "module Testing where"
1548 , "module Testing where"
1491 , "y = x + z" -- plugin replaces x with y, making this have only one diagnostic
1549 , "y = x + z" -- plugin replaces x with y, making this have only one diagnostic
1493− _ <- openDoc' "Testing.hs" "haskell" content
1551+ _ <- createDoc "Testing.hs" "haskell" content
1494 expectDiagnostics
1552 expectDiagnostics
1495 [ ( "Testing.hs",
1553 [ ( "Testing.hs",
1496 [(DsError, (2, 8), "Variable not in scope: z")]
1554 [(DsError, (2, 8), "Variable not in scope: z")]
@@ -1523,8 +1581,8 @@ safeTests =
1523 ,"safeId = trustWorthyId"
1581 ,"safeId = trustWorthyId"
1526− _ <- openDoc' "A.hs" "haskell" sourceA
1584+ _ <- createDoc "A.hs" "haskell" sourceA
1527− _ <- openDoc' "B.hs" "haskell" sourceB
1585+ _ <- createDoc "B.hs" "haskell" sourceB
1528 expectNoMoreDiagnostics 1 ]
1586 expectNoMoreDiagnostics 1 ]
1530thTests :: TestTree
1588thTests :: TestTree
@@ -1552,8 +1610,8 @@ thTests =
1552 "b :: Integer",
1610 "b :: Integer",
1553 "b = $(litE $ IntegerL $ a) + n"
1611 "b = $(litE $ IntegerL $ a) + n"
1555− _ <- openDoc' "A.hs" "haskell" sourceA
1613+ _ <- createDoc "A.hs" "haskell" sourceA
1556− _ <- openDoc' "B.hs" "haskell" sourceB
1614+ _ <- createDoc "B.hs" "haskell" sourceB
1557 expectDiagnostics [ ( "B.hs", [(DsError, (6, 29), "Variable not in scope: n")] ) ]
1615 expectDiagnostics [ ( "B.hs", [(DsError, (6, 29), "Variable not in scope: n")] ) ]
1558 , testSessionWait "newtype-closure" $ do
1616 , testSessionWait "newtype-closure" $ do
1559 let sourceA =
1617 let sourceA =
@@ -1573,8 +1631,8 @@ thTests =
1573 ,"import A"
1631 ,"import A"
1574 ,"b :: Int"
1632 ,"b :: Int"
1575 ,"b = $( a )" ]
1633 ,"b = $( a )" ]
1576− _ <- openDoc' "A.hs" "haskell" sourceA
1634+ _ <- createDoc "A.hs" "haskell" sourceA
1577− _ <- openDoc' "B.hs" "haskell" sourceB
1635+ _ <- createDoc "B.hs" "haskell" sourceB
1578 return ()
1636 return ()
@@ -1583,7 +1641,7 @@ completionTests
1583 = testGroup "completion"
1641 = testGroup "completion"
1584 [ testSessionWait "variable" $ do
1642 [ testSessionWait "variable" $ do
1585 let source = T.unlines ["module A where", "f = hea"]
1643 let source = T.unlines ["module A where", "f = hea"]
1586− docId <- openDoc' "A.hs" "haskell" source
1644+ docId <- createDoc "A.hs" "haskell" source
1587 compls <- getCompletions docId (Position 1 7)
1645 compls <- getCompletions docId (Position 1 7)
1588 liftIO $ map dropDocs compls @?=
1646 liftIO $ map dropDocs compls @?=
1589 [complItem "head" (Just CiFunction) (Just "[a] -> a")]
1647 [complItem "head" (Just CiFunction) (Just "[a] -> a")]
@@ -1595,7 +1653,7 @@ completionTests
1596 , testSessionWait "constructor" $ do
1654 , testSessionWait "constructor" $ do
1597 let source = T.unlines ["module A where", "f = Tru"]
1655 let source = T.unlines ["module A where", "f = Tru"]
1598− docId <- openDoc' "A.hs" "haskell" source
1656+ docId <- createDoc "A.hs" "haskell" source
1599 compls <- getCompletions docId (Position 1 7)
1657 compls <- getCompletions docId (Position 1 7)
1600 liftIO $ map dropDocs compls @?=
1658 liftIO $ map dropDocs compls @?=
1601 [ complItem "True" (Just CiConstructor) (Just "Bool")
1659 [ complItem "True" (Just CiConstructor) (Just "Bool")
@@ -1607,7 +1665,7 @@ completionTests
1608 , testSessionWait "type" $ do
1666 , testSessionWait "type" $ do
1609 let source = T.unlines ["{-# OPTIONS_GHC -Wall #-}", "module A () where", "f :: ()", "f = ()"]
1667 let source = T.unlines ["{-# OPTIONS_GHC -Wall #-}", "module A () where", "f :: ()", "f = ()"]
1610− docId <- openDoc' "A.hs" "haskell" source
1668+ docId <- createDoc "A.hs" "haskell" source
1611 expectDiagnostics [ ("A.hs", [(DsWarning, (3,0), "not used")]) ]
1669 expectDiagnostics [ ("A.hs", [(DsWarning, (3,0), "not used")]) ]
1612 changeDoc docId [TextDocumentContentChangeEvent Nothing Nothing $ T.unlines ["{-# OPTIONS_GHC -Wall #-}", "module A () where", "f :: Bo", "f = True"]]
1670 changeDoc docId [TextDocumentContentChangeEvent Nothing Nothing $ T.unlines ["{-# OPTIONS_GHC -Wall #-}", "module A () where", "f :: Bo", "f = True"]]
1613 compls <- getCompletions docId (Position 2 7)
1671 compls <- getCompletions docId (Position 2 7)
@@ -1624,7 +1682,7 @@ completionTests
1624 checkDocText "Bool" boolDocs [ "Defined in 'Prelude'" ]
1682 checkDocText "Bool" boolDocs [ "Defined in 'Prelude'" ]
1625 , testSessionWait "qualified" $ do
1683 , testSessionWait "qualified" $ do
1626 let source = T.unlines ["{-# OPTIONS_GHC -Wunused-binds #-}", "module A () where", "f = ()"]
1684 let source = T.unlines ["{-# OPTIONS_GHC -Wunused-binds #-}", "module A () where", "f = ()"]
1627− docId <- openDoc' "A.hs" "haskell" source
1685+ docId <- createDoc "A.hs" "haskell" source
1628 expectDiagnostics [ ("A.hs", [(DsWarning, (2, 0), "not used")]) ]
1686 expectDiagnostics [ ("A.hs", [(DsWarning, (2, 0), "not used")]) ]
1629 changeDoc docId [TextDocumentContentChangeEvent Nothing Nothing $ T.unlines ["{-# OPTIONS_GHC -Wunused-binds #-}", "module A () where", "f = Prelude.hea"]]
1687 changeDoc docId [TextDocumentContentChangeEvent Nothing Nothing $ T.unlines ["{-# OPTIONS_GHC -Wunused-binds #-}", "module A () where", "f = Prelude.hea"]]
1630 compls <- getCompletions docId (Position 2 15)
1688 compls <- getCompletions docId (Position 2 15)
@@ -1638,7 +1696,7 @@ completionTests
1639 , testSessionWait "keyword" $ do
1697 , testSessionWait "keyword" $ do
1640 let source = T.unlines ["module A where", "f = newty"]
1698 let source = T.unlines ["module A where", "f = newty"]
1641− docId <- openDoc' "A.hs" "haskell" source
1699+ docId <- createDoc "A.hs" "haskell" source
1642 compls <- getCompletions docId (Position 1 9)
1700 compls <- getCompletions docId (Position 1 9)
1643 liftIO $ compls @?= [keywordItem "newtype"]
1701 liftIO $ compls @?= [keywordItem "newtype"]
1644 , testSessionWait "type context" $ do
1702 , testSessionWait "type context" $ do
@@ -1647,7 +1705,7 @@ completionTests
1647 , "module A () where"
1705 , "module A () where"
1648 , "f = f"
1706 , "f = f"
1650− docId <- openDoc' "A.hs" "haskell" source
1708+ docId <- createDoc "A.hs" "haskell" source
1651 expectDiagnostics [("A.hs", [(DsWarning, (2, 0), "not used")])]
1709 expectDiagnostics [("A.hs", [(DsWarning, (2, 0), "not used")])]
1652 changeDoc docId
1710 changeDoc docId
1653 [ TextDocumentContentChangeEvent Nothing Nothing $ T.unlines
1711 [ TextDocumentContentChangeEvent Nothing Nothing $ T.unlines
@@ -1715,7 +1773,7 @@ outlineTests = testGroup
1715 "outline"
1773 "outline"
1716 [ testSessionWait "type class" $ do
1774 [ testSessionWait "type class" $ do
1717 let source = T.unlines ["module A where", "class A a where a :: a -> Bool"]
1775 let source = T.unlines ["module A where", "class A a where a :: a -> Bool"]
1718− docId <- openDoc' "A.hs" "haskell" source
1776+ docId <- createDoc "A.hs" "haskell" source
1719 symbols <- getDocumentSymbols docId
1777 symbols <- getDocumentSymbols docId
1720 liftIO $ symbols @?= Left
1778 liftIO $ symbols @?= Left
1721 [ moduleSymbol
1779 [ moduleSymbol
@@ -1728,7 +1786,7 @@ outlineTests = testGroup
1729 , testSessionWait "type class instance " $ do
1787 , testSessionWait "type class instance " $ do
1730 let source = T.unlines ["class A a where", "instance A () where"]
1788 let source = T.unlines ["class A a where", "instance A () where"]
1731− docId <- openDoc' "A.hs" "haskell" source
1789+ docId <- createDoc "A.hs" "haskell" source
1732 symbols <- getDocumentSymbols docId
1790 symbols <- getDocumentSymbols docId
1733 liftIO $ symbols @?= Left
1791 liftIO $ symbols @?= Left
1734 [ classSymbol "A a" (R 0 0 0 15) []
1792 [ classSymbol "A a" (R 0 0 0 15) []
@@ -1736,7 +1794,7 @@ outlineTests = testGroup
1737 , testSessionWait "type family" $ do
1795 , testSessionWait "type family" $ do
1738 let source = T.unlines ["{-# language TypeFamilies #-}", "type family A"]
1796 let source = T.unlines ["{-# language TypeFamilies #-}", "type family A"]
1739− docId <- openDoc' "A.hs" "haskell" source
1797+ docId <- createDoc "A.hs" "haskell" source
1740 symbols <- getDocumentSymbols docId
1798 symbols <- getDocumentSymbols docId
1741 liftIO $ symbols @?= Left [docSymbolD "A" "type family" SkClass (R 1 0 1 13)]
1799 liftIO $ symbols @?= Left [docSymbolD "A" "type family" SkClass (R 1 0 1 13)]
1742 , testSessionWait "type family instance " $ do
1800 , testSessionWait "type family instance " $ do
@@ -1745,7 +1803,7 @@ outlineTests = testGroup
1745 , "type family A a"
1803 , "type family A a"
1746 , "type instance A () = ()"
1804 , "type instance A () = ()"
1748− docId <- openDoc' "A.hs" "haskell" source
1806+ docId <- createDoc "A.hs" "haskell" source
1749 symbols <- getDocumentSymbols docId
1807 symbols <- getDocumentSymbols docId
1750 liftIO $ symbols @?= Left
1808 liftIO $ symbols @?= Left
1751 [ docSymbolD "A a" "type family" SkClass (R 1 0 1 15)
1809 [ docSymbolD "A a" "type family" SkClass (R 1 0 1 15)
@@ -1753,7 +1811,7 @@ outlineTests = testGroup
1754 , testSessionWait "data family" $ do
1812 , testSessionWait "data family" $ do
1755 let source = T.unlines ["{-# language TypeFamilies #-}", "data family A"]
1813 let source = T.unlines ["{-# language TypeFamilies #-}", "data family A"]
1756− docId <- openDoc' "A.hs" "haskell" source
1814+ docId <- createDoc "A.hs" "haskell" source
1757 symbols <- getDocumentSymbols docId
1815 symbols <- getDocumentSymbols docId
1758 liftIO $ symbols @?= Left [docSymbolD "A" "data family" SkClass (R 1 0 1 11)]
1816 liftIO $ symbols @?= Left [docSymbolD "A" "data family" SkClass (R 1 0 1 11)]
1759 , testSessionWait "data family instance " $ do
1817 , testSessionWait "data family instance " $ do
@@ -1762,7 +1820,7 @@ outlineTests = testGroup
1762 , "data family A a"
1820 , "data family A a"
1763 , "data instance A () = A ()"
1821 , "data instance A () = A ()"
1765− docId <- openDoc' "A.hs" "haskell" source
1823+ docId <- createDoc "A.hs" "haskell" source
1766 symbols <- getDocumentSymbols docId
1824 symbols <- getDocumentSymbols docId
1767 liftIO $ symbols @?= Left
1825 liftIO $ symbols @?= Left
1768 [ docSymbolD "A a" "data family" SkClass (R 1 0 1 11)
1826 [ docSymbolD "A a" "data family" SkClass (R 1 0 1 11)
@@ -1770,36 +1828,36 @@ outlineTests = testGroup
1771 , testSessionWait "constant" $ do
1829 , testSessionWait "constant" $ do
1772 let source = T.unlines ["a = ()"]
1830 let source = T.unlines ["a = ()"]
1773− docId <- openDoc' "A.hs" "haskell" source
1831+ docId <- createDoc "A.hs" "haskell" source
1774 symbols <- getDocumentSymbols docId
1832 symbols <- getDocumentSymbols docId
1775 liftIO $ symbols @?= Left
1833 liftIO $ symbols @?= Left
1776 [docSymbol "a" SkFunction (R 0 0 0 6)]
1834 [docSymbol "a" SkFunction (R 0 0 0 6)]
1777 , testSessionWait "pattern" $ do
1835 , testSessionWait "pattern" $ do
1778 let source = T.unlines ["Just foo = Just 21"]
1836 let source = T.unlines ["Just foo = Just 21"]
1779− docId <- openDoc' "A.hs" "haskell" source
1837+ docId <- createDoc "A.hs" "haskell" source
1780 symbols <- getDocumentSymbols docId
1838 symbols <- getDocumentSymbols docId
1781 liftIO $ symbols @?= Left
1839 liftIO $ symbols @?= Left
1782 [docSymbol "Just foo" SkFunction (R 0 0 0 18)]
1840 [docSymbol "Just foo" SkFunction (R 0 0 0 18)]
1783 , testSessionWait "pattern with type signature" $ do
1841 , testSessionWait "pattern with type signature" $ do
1784 let source = T.unlines ["{-# language ScopedTypeVariables #-}", "a :: () = ()"]
1842 let source = T.unlines ["{-# language ScopedTypeVariables #-}", "a :: () = ()"]
1785− docId <- openDoc' "A.hs" "haskell" source
1843+ docId <- createDoc "A.hs" "haskell" source
1786 symbols <- getDocumentSymbols docId
1844 symbols <- getDocumentSymbols docId
1787 liftIO $ symbols @?= Left
1845 liftIO $ symbols @?= Left
1788 [docSymbol "a :: ()" SkFunction (R 1 0 1 12)]
1846 [docSymbol "a :: ()" SkFunction (R 1 0 1 12)]
1789 , testSessionWait "function" $ do
1847 , testSessionWait "function" $ do
1790 let source = T.unlines ["a x = ()"]
1848 let source = T.unlines ["a x = ()"]
1791− docId <- openDoc' "A.hs" "haskell" source
1849+ docId <- createDoc "A.hs" "haskell" source
1792 symbols <- getDocumentSymbols docId
1850 symbols <- getDocumentSymbols docId
1793 liftIO $ symbols @?= Left [docSymbol "a" SkFunction (R 0 0 0 8)]
1851 liftIO $ symbols @?= Left [docSymbol "a" SkFunction (R 0 0 0 8)]
1794 , testSessionWait "type synonym" $ do
1852 , testSessionWait "type synonym" $ do
1795 let source = T.unlines ["type A = Bool"]
1853 let source = T.unlines ["type A = Bool"]
1796− docId <- openDoc' "A.hs" "haskell" source
1854+ docId <- createDoc "A.hs" "haskell" source
1797 symbols <- getDocumentSymbols docId
1855 symbols <- getDocumentSymbols docId
1798 liftIO $ symbols @?= Left
1856 liftIO $ symbols @?= Left
1799 [docSymbol' "A" SkTypeParameter (R 0 0 0 13) (R 0 5 0 6)]
1857 [docSymbol' "A" SkTypeParameter (R 0 0 0 13) (R 0 5 0 6)]
1800 , testSessionWait "datatype" $ do
1858 , testSessionWait "datatype" $ do
1801 let source = T.unlines ["data A = C"]
1859 let source = T.unlines ["data A = C"]
1802− docId <- openDoc' "A.hs" "haskell" source
1860+ docId <- createDoc "A.hs" "haskell" source
1803 symbols <- getDocumentSymbols docId
1861 symbols <- getDocumentSymbols docId
1804 liftIO $ symbols @?= Left
1862 liftIO $ symbols @?= Left
1805 [ docSymbolWithChildren "A"
1863 [ docSymbolWithChildren "A"
@@ -1807,9 +1865,21 @@ outlineTests = testGroup
1807 (R 0 0 0 10)
1865 (R 0 0 0 10)
1808 [docSymbol "C" SkConstructor (R 0 9 0 10)]
1866 [docSymbol "C" SkConstructor (R 0 9 0 10)]
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)
1810 , testSessionWait "import" $ do
1880 , testSessionWait "import" $ do
1811 let source = T.unlines ["import Data.Maybe"]
1881 let source = T.unlines ["import Data.Maybe"]
1812− docId <- openDoc' "A.hs" "haskell" source
1882+ docId <- createDoc "A.hs" "haskell" source
1813 symbols <- getDocumentSymbols docId
1883 symbols <- getDocumentSymbols docId
1814 liftIO $ symbols @?= Left
1884 liftIO $ symbols @?= Left
1815 [docSymbolWithChildren "imports"
1885 [docSymbolWithChildren "imports"
@@ -1820,7 +1890,7 @@ outlineTests = testGroup
1821 , testSessionWait "multiple import" $ do
1891 , testSessionWait "multiple import" $ do
1822 let source = T.unlines ["", "import Data.Maybe", "", "import Control.Exception", ""]
1892 let source = T.unlines ["", "import Data.Maybe", "", "import Control.Exception", ""]
1823− docId <- openDoc' "A.hs" "haskell" source
1893+ docId <- createDoc "A.hs" "haskell" source
1824 symbols <- getDocumentSymbols docId
1894 symbols <- getDocumentSymbols docId
1825 liftIO $ symbols @?= Left
1895 liftIO $ symbols @?= Left
1826 [docSymbolWithChildren "imports"
1896 [docSymbolWithChildren "imports"
@@ -1835,7 +1905,7 @@ outlineTests = testGroup
1835 [ "{-# language ForeignFunctionInterface #-}"
1905 [ "{-# language ForeignFunctionInterface #-}"
1836 , "foreign import ccall \"a\" a :: Int"
1906 , "foreign import ccall \"a\" a :: Int"
1838− docId <- openDoc' "A.hs" "haskell" source
1908+ docId <- createDoc "A.hs" "haskell" source
1839 symbols <- getDocumentSymbols docId
1909 symbols <- getDocumentSymbols docId
1840 liftIO $ symbols @?= Left [docSymbolD "a" "import" SkObject (R 1 0 1 33)]
1910 liftIO $ symbols @?= Left [docSymbolD "a" "import" SkObject (R 1 0 1 33)]
1841 , testSessionWait "foreign export" $ do
1911 , testSessionWait "foreign export" $ do
@@ -1843,7 +1913,7 @@ outlineTests = testGroup
1843 [ "{-# language ForeignFunctionInterface #-}"
1913 [ "{-# language ForeignFunctionInterface #-}"
1844 , "foreign export ccall odd :: Int -> Bool"
1914 , "foreign export ccall odd :: Int -> Bool"
1846− docId <- openDoc' "A.hs" "haskell" source
1916+ docId <- createDoc "A.hs" "haskell" source
1847 symbols <- getDocumentSymbols docId
1917 symbols <- getDocumentSymbols docId
1848 liftIO $ symbols @?= Left [docSymbolD "odd" "export" SkObject (R 1 0 1 39)]
1918 liftIO $ symbols @?= Left [docSymbolD "odd" "export" SkObject (R 1 0 1 39)]
@@ -1856,6 +1926,8 @@ outlineTests = testGroup
1856 DocumentSymbol name (Just detail) kind Nothing loc loc Nothing
1926 DocumentSymbol name (Just detail) kind Nothing loc loc Nothing
1857 docSymbolWithChildren name kind loc cc =
1927 docSymbolWithChildren name kind loc cc =
1858 DocumentSymbol name Nothing kind Nothing loc loc (Just $ List cc)
1928 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)
1859 moduleSymbol name loc cc = DocumentSymbol name
1931 moduleSymbol name loc cc = DocumentSymbol name
@@ -1877,6 +1949,13 @@ pattern R x y x' y' = Range (Position x y) (Position x' y')
1877xfail :: TestTree -> String -> TestTree
1949xfail :: TestTree -> String -> TestTree
1878xfail = flip expectFailBecause
1950xfail = flip expectFailBecause
1952+xfail8101 :: TestTree -> String -> TestTree
1953+#if MIN_GHC_API_VERSION(8,10,0)
1954+xfail8101 = flip expectFailBecause
1880data Expect
1959data Expect
1881 = ExpectRange Range -- Both gotoDef and hover should report this range
1960 = ExpectRange Range -- Both gotoDef and hover should report this range
1882 | ExpectLocation Location
1961 | ExpectLocation Location
@@ -1954,6 +2033,7 @@ cradleTests :: TestTree
1954cradleTests = testGroup "cradle"
2033cradleTests = testGroup "cradle"
1955 [testGroup "dependencies" [sessionDepsArePickedUp]
2034 [testGroup "dependencies" [sessionDepsArePickedUp]
1956 ,testGroup "loading" [loadCradleOnlyonce]
2035 ,testGroup "loading" [loadCradleOnlyonce]
2036+ ,testGroup "multi" [simpleMultiTest, simpleMultiTest2]
1959loadCradleOnlyonce :: TestTree
2039loadCradleOnlyonce :: TestTree
@@ -1968,17 +2048,53 @@ loadCradleOnlyonce = testGroup "load cradle only once"
1968 test dir
2048 test dir
1969 implicit dir = test dir
2049 implicit dir = test dir
1970 test _dir = do
2050 test _dir = do
1971− doc <- openDoc' "B.hs" "haskell" "module B where\nimport Data.Foo"
2051+ doc <- createDoc "B.hs" "haskell" "module B where\nimport Data.Foo"
1972 msgs <- someTill (skipManyTill anyMessage cradleLoadedMessage) (skipManyTill anyMessage (message @PublishDiagnosticsNotification))
2052 msgs <- someTill (skipManyTill anyMessage cradleLoadedMessage) (skipManyTill anyMessage (message @PublishDiagnosticsNotification))
1973 liftIO $ length msgs @?= 1
2053 liftIO $ length msgs @?= 1
1974 changeDoc doc [TextDocumentContentChangeEvent Nothing Nothing "module B where\nimport Data.Maybe"]
2054 changeDoc doc [TextDocumentContentChangeEvent Nothing Nothing "module B where\nimport Data.Maybe"]
1975 msgs <- manyTill (skipManyTill anyMessage cradleLoadedMessage) (skipManyTill anyMessage (message @PublishDiagnosticsNotification))
2055 msgs <- manyTill (skipManyTill anyMessage cradleLoadedMessage) (skipManyTill anyMessage (message @PublishDiagnosticsNotification))
1976 liftIO $ length msgs @?= 0
2056 liftIO $ length msgs @?= 0
1977− _ <- openDoc' "A.hs" "haskell" "module A where\nimport LoadCradleBar"
2057+ _ <- createDoc "A.hs" "haskell" "module A where\nimport LoadCradleBar"
1978 msgs <- manyTill (skipManyTill anyMessage cradleLoadedMessage) (skipManyTill anyMessage (message @PublishDiagnosticsNotification))
2058 msgs <- manyTill (skipManyTill anyMessage cradleLoadedMessage) (skipManyTill anyMessage (message @PublishDiagnosticsNotification))
1979 liftIO $ length msgs @?= 0
2059 liftIO $ length msgs @?= 0
2062+dependentFileTest :: TestTree
2063+dependentFileTest = testGroup "addDependentFile"
2064+ [testGroup "file-changed" [testSession' "test" test]
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"
2077+ , " qAddDependentFile \"dep-file.txt\""
2078+ , " f <- qRunIO (readFile \"dep-file.txt\")"
2079+ , " if f == \"B\" then [| 1 |] else lift f)"
2081+ let bazContent = T.unlines ["module Baz where", "import Foo"]
2082+ _ <-createDoc "Foo.hs" "haskell" fooContent
2083+ doc <- createDoc "Baz.hs" "haskell" bazContent
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
2093+ -- Modifying Baz will now trigger Foo to be rebuilt as well
2094+ changeDoc doc [change]
2095+ expectDiagnostics [("Foo.hs", [])]
1982cradleLoadedMessage :: Session FromServerMessage
2098cradleLoadedMessage :: Session FromServerMessage
1983cradleLoadedMessage = satisfy $ \case
2099cradleLoadedMessage = satisfy $ \case
1984 NotCustomServer (NotificationMessage _ (CustomServerMethod m) _) -> m == cradleLoadedMethod
2100 NotCustomServer (NotificationMessage _ (CustomServerMethod m) _) -> m == cradleLoadedMethod
@@ -1987,6 +2103,56 @@ cradleLoadedMessage = satisfy $ \case
1987cradleLoadedMethod :: T.Text
2103cradleLoadedMethod :: T.Text
1988cradleLoadedMethod = "ghcide/cradle/loaded"
2104cradleLoadedMethod = "ghcide/cradle/loaded"
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
2111+ (mapM getEnv vars >>= \prevState -> mapM_ unsetEnv vars >> pure prevState)
2112+ (\prevState -> mapM_ (\(var, value) -> restore var value) (zip vars prevState))
2115+ [ "GHC_PACKAGE_PATH"
2116+ , "GHC_ENVIRONMENT"
2117+ , "HASKELL_DIST_DIR"
2118+ , "HASKELL_PACKAGE_SANDBOX"
2119+ , "HASKELL_PACKAGE_SANDBOXES"
2121+ restore var Nothing = unsetEnv var
2122+ restore var (Just val) = setEnv var val True
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
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
1990sessionDepsArePickedUp :: TestTree
2156sessionDepsArePickedUp :: TestTree
1991sessionDepsArePickedUp = testSession'
2157sessionDepsArePickedUp = testSession'
1992 "session-deps-are-picked-up"
2158 "session-deps-are-picked-up"
@@ -1996,7 +2162,7 @@ sessionDepsArePickedUp = testSession'
1996 (dir </> "hie.yaml")
2162 (dir </> "hie.yaml")
1997 "cradle: {direct: {arguments: []}}"
2163 "cradle: {direct: {arguments: []}}"
1998 -- Open without OverloadedStrings and expect an error.
2164 -- Open without OverloadedStrings and expect an error.
1999− doc <- openDoc' "Foo.hs" "haskell" fooContent
2165+ doc <- createDoc "Foo.hs" "haskell" fooContent
2000 expectDiagnostics
2166 expectDiagnostics
2001 [("Foo.hs", [(DsError, (3, 6), "Couldn't match expected type")])]
2167 [("Foo.hs", [(DsError, (3, 6), "Couldn't match expected type")])]
2002 -- Update hie.yaml to enable OverloadedStrings.
2168 -- Update hie.yaml to enable OverloadedStrings.
@@ -2031,6 +2197,9 @@ sessionDepsArePickedUp = testSession'
2031testSession :: String -> Session () -> TestTree
2197testSession :: String -> Session () -> TestTree
2032testSession name = testCase name . run
2198testSession name = testCase name . run
2200+testSessionWithExtraFiles :: FilePath -> String -> (FilePath -> Session ()) -> TestTree
2201+testSessionWithExtraFiles prefix name = testCase name . runWithExtraFiles prefix
2034testSession' :: String -> (FilePath -> Session ()) -> TestTree
2203testSession' :: String -> (FilePath -> Session ()) -> TestTree
2035testSession' name = testCase name . run'
2204testSession' name = testCase name . run'
@@ -2065,6 +2234,19 @@ mkRange a b c d = Range (Position a b) (Position c d)
2065run :: Session a -> IO a
2234run :: Session a -> IO a
2066run s = withTempDir $ \dir -> runInDir dir s
2235run s = withTempDir $ \dir -> runInDir dir s
2237+runWithExtraFiles :: FilePath -> (FilePath -> Session a) -> IO a
2238+runWithExtraFiles prefix s = withTempDir $ \dir -> do
2239+ copyTestDataFiles dir prefix
2240+ runInDir dir (s dir)
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)
2068run' :: (FilePath -> Session a) -> IO a
2250run' :: (FilePath -> Session a) -> IO a
2069run' s = withTempDir $ \dir -> runInDir dir (s dir)
2251run' s = withTempDir $ \dir -> runInDir dir (s dir)
@@ -2076,11 +2258,6 @@ runInDir dir s = do
2076 -- since the package import test creates "Data/List.hs", which otherwise has no physical home
2258 -- since the package import test creates "Data/List.hs", which otherwise has no physical home
2077 createDirectoryIfMissing True $ dir ++ "/Data"
2259 createDirectoryIfMissing True $ dir ++ "/Data"
2079− -- Copy all the test data files to the temporary workspace
2080− testDataFiles <- getDirectoryFilesIO "test/data" ["//*"]
2081− for_ testDataFiles $ \f -> do
2082− createDirectoryIfMissing True $ dir </> takeDirectory f
2083− copyFile ("test/data" </> f) (dir </> f)
2085 let cmd = unwords [ghcideExe, "--lsp", "--test", "--cwd", dir]
2262 let cmd = unwords [ghcideExe, "--lsp", "--test", "--cwd", dir]
2086 -- HIE calls getXgdDirectory which assumes that HOME is set.
2263 -- HIE calls getXgdDirectory which assumes that HOME is set.
@@ -2099,7 +2276,7 @@ runInDir dir s = do
2099openTestDataDoc :: FilePath -> Session TextDocumentIdentifier
2276openTestDataDoc :: FilePath -> Session TextDocumentIdentifier
2100openTestDataDoc path = do
2277openTestDataDoc path = do
2101 source <- liftIO $ readFileUtf8 $ "test/data" </> path
2278 source <- liftIO $ readFileUtf8 $ "test/data" </> path
2102− openDoc' path "haskell" source
2279+ createDoc path "haskell" source
2104findCodeActions :: TextDocumentIdentifier -> Range -> [T.Text] -> Session [CodeAction]
2281findCodeActions :: TextDocumentIdentifier -> Range -> [T.Text] -> Session [CodeAction]
2105findCodeActions doc range expectedTitles = do
2282findCodeActions doc range expectedTitles = do
@@ -2140,20 +2317,6 @@ unitTests = do
2140 uriToFilePath' uri @?= Just ""
2317 uriToFilePath' uri @?= Just ""
2143−-- | Wrapper around 'LSPTest.openDoc'' that sends file creation events
2144−openDoc' :: FilePath -> String -> T.Text -> Session TextDocumentIdentifier
2145−openDoc' fp name contents = do
2146− res@(TextDocumentIdentifier uri) <- LSPTest.openDoc' fp name contents
2147− -- Needed as ghcide sets up and relies on WatchedFiles but lsp-test does not track them
2148− sendNotification WorkspaceDidChangeWatchedFiles (DidChangeWatchedFilesParams $ List [FileEvent uri FcCreated])
2151−-- | Version of 'LSPTest.openDoc'' that does not send WatchedFiles events for files outside the workspace
2152−openDoc'' :: FilePath -> String -> T.Text -> Session TextDocumentIdentifier
2153−-- At the moment this is just LSPTest.openDoc' but it may change in the future
2154−-- when/if lsp-test implements WatchedFiles
2155−openDoc'' = LSPTest.openDoc'
2157positionMappingTests :: TestTree
2320positionMappingTests :: TestTree
2158positionMappingTests =
2321positionMappingTests =
2159 testGroup "position mapping"
2322 testGroup "position mapping"
@@ -2323,11 +2486,11 @@ nthLine i r
2323 | i >= Rope.rows r = error $ "Row number out of bounds: " <> show i <> "/" <> show (Rope.rows r)
2486 | i >= Rope.rows r = error $ "Row number out of bounds: " <> show i <> "/" <> show (Rope.rows r)
2324 | otherwise = Rope.takeWhile (/= '\n') $ fst $ Rope.splitAtLine 1 $ snd $ Rope.splitAtLine (i - 1) r
2487 | otherwise = Rope.takeWhile (/= '\n') $ fst $ Rope.splitAtLine 1 $ snd $ Rope.splitAtLine (i - 1) r
2326−getWatchedFilesSubscriptionsUntilProgressEnd :: Session [Maybe Value]
2489+getWatchedFilesSubscriptionsUntil :: forall end . (FromJSON end, Typeable end) => Session [Maybe Value]
2327−getWatchedFilesSubscriptionsUntilProgressEnd = do
2490+getWatchedFilesSubscriptionsUntil = do
2328− msgs <- manyTill (Just <$> message @RegisterCapabilityRequest <|> Nothing <$ anyMessage) (message @WorkDoneProgressEndNotification)
2491+ msgs <- manyTill (Just <$> message @RegisterCapabilityRequest <|> Nothing <$ anyMessage) (message @end)
2331− | Just (RequestMessage{_params = RegistrationParams (List regs)}) <- msgs
2494+ | Just RequestMessage{_params = RegistrationParams (List regs)} <- msgs
2332 , Registration _id WorkspaceDidChangeWatchedFiles args <- regs
2495 , Registration _id WorkspaceDidChangeWatchedFiles args <- regs