@@ -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,53 +10,74 @@
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.Char (toLower)
17+import Data.Aeson (FromJSON, Value)
16import Data.Foldable
18import Data.Foldable
17−import Data.List
19+import Data.List.Extra
20+import Data.Rope.UTF16 (Rope)
21+import qualified Data.Rope.UTF16 as Rope
22+import Development.IDE.Core.PositionMapping (fromCurrent, toCurrent)
18import Development.IDE.GHC.Util
23import Development.IDE.GHC.Util
19import qualified Data.Text as T
24import qualified Data.Text as T
20import Development.IDE.Spans.Common
26import Development.IDE.Spans.Common
21import Development.IDE.Test
27import Development.IDE.Test
22import Development.IDE.Test.Runfiles
28import Development.IDE.Test.Runfiles
23import Development.IDE.Types.Location
29import Development.IDE.Types.Location
24−import qualified Language.Haskell.LSP.Test as LSPTest
30+import Development.Shake (getDirectoryFilesIO)
25−import Language.Haskell.LSP.Test hiding (openDoc')
31+import Language.Haskell.LSP.Test
32+import Language.Haskell.LSP.Messages
26import Language.Haskell.LSP.Types
33import Language.Haskell.LSP.Types
27import Language.Haskell.LSP.Types.Capabilities
34import Language.Haskell.LSP.Types.Capabilities
35+import qualified Language.Haskell.LSP.Types.Lens as Lsp (diagnostics, params, message)
36+import Language.Haskell.LSP.VFS (applyChange)
28−import System.Environment.Blank (setEnv)
38+import System.Environment.Blank (getEnv, setEnv, unsetEnv)
29import System.FilePath
39import System.FilePath
30import System.IO.Extra
40import System.IO.Extra
31import System.Directory
41import System.Directory
42+import Test.QuickCheck
43+import Test.QuickCheck.Instances ()
32import Test.Tasty
44import Test.Tasty
33−import Test.Tasty.HUnit
34import Test.Tasty.ExpectedFailure
45import Test.Tasty.ExpectedFailure
46+import Test.Tasty.Ingredients.Rerun
47+import Test.Tasty.HUnit
48+import Test.Tasty.QuickCheck
35import Data.Maybe
49import Data.Maybe
37main :: IO ()
51main :: IO ()
53+ -- We mess with env vars so run single-threaded.
54+ setEnv "TASTY_NUM_THREADS" "1" True
38−main = defaultMain $ testGroup "HIE"
55+ defaultMainWithRerun $ testGroup "HIE"
39− [ testSession "open close" $ do
56+ [ testSession "open close" $ do
40− doc <- openDoc' "Testing.hs" "haskell" ""
57+ doc <- createDoc "Testing.hs" "haskell" ""
41− void (message :: Session WorkDoneProgressCreateRequest)
58+ void (skipManyTill anyMessage message :: Session WorkDoneProgressCreateRequest)
42− void (message :: Session WorkDoneProgressBeginNotification)
59+ void (skipManyTill anyMessage message :: Session WorkDoneProgressBeginNotification)
43− closeDoc doc
60+ closeDoc doc
44− void (message :: Session WorkDoneProgressEndNotification)
61+ void (skipManyTill anyMessage message :: Session WorkDoneProgressEndNotification)
45− , initializeResponseTests
62+ , initializeResponseTests
46− , completionTests
63+ , completionTests
47− , cppTests
64+ , cppTests
48− , diagnosticTests
65+ , diagnosticTests
49− , codeActionTests
66+ , codeActionTests
50− , codeLensesTests
67+ , codeLensesTests
51− , outlineTests
68+ , outlineTests
52− , findDefinitionAndHoverTests
69+ , findDefinitionAndHoverTests
53− , pluginTests
70+ , pluginTests
54− , preprocessorTests
71+ , preprocessorTests
55− , thTests
72+ , thTests
56− , unitTests
74+ , unitTests
57− , haddockTests
75+ , haddockTests
76+ , positionMappingTests
60initializeResponseTests :: TestTree
82initializeResponseTests :: TestTree
61initializeResponseTests = withResource acquire release tests where
83initializeResponseTests = withResource acquire release tests where
@@ -90,8 +112,8 @@ initializeResponseTests = withResource acquire release tests where
90 , chk "NO doc link" _documentLinkProvider Nothing
112 , chk "NO doc link" _documentLinkProvider Nothing
91 , chk "NO color" _colorProvider (Just $ ColorOptionsStatic False)
113 , chk "NO color" _colorProvider (Just $ ColorOptionsStatic False)
92 , chk "NO folding range" _foldingRangeProvider (Just $ FoldingRangeOptionsStatic False)
114 , chk "NO folding range" _foldingRangeProvider (Just $ FoldingRangeOptionsStatic False)
93− , chk " execute command" _executeCommandProvider (Just $ ExecuteCommandOptions $ List ["typesignature.add"])
115+ , che " execute command" _executeCommandProvider (Just $ ExecuteCommandOptions $ List ["typesignature.add"])
94− , chk "NO workspace" _workspace nothingWorkspace
116+ , chk " workspace" _workspace (Just $ WorkspaceOptions (Just WorkspaceFolderOptions{_supported = Just True, _changeNotifications = Just ( WorkspaceFolderChangeNotificationsBool True )}))
95 , chk "NO experimental" _experimental Nothing
117 , chk "NO experimental" _experimental Nothing
@@ -102,14 +124,21 @@ initializeResponseTests = withResource acquire release tests where
102 , _willSaveWaitUntil = Nothing
124 , _willSaveWaitUntil = Nothing
103 , _save = Just (SaveOptions {_includeText = Nothing})}))
125 , _save = Just (SaveOptions {_includeText = Nothing})}))
105− nothingWorkspace = Just (WorkspaceOptions {_workspaceFolders = Nothing})
107 chk :: (Eq a, Show a) => TestName -> (InitializeResponseCapabilitiesInner -> a) -> a -> TestTree
127 chk :: (Eq a, Show a) => TestName -> (InitializeResponseCapabilitiesInner -> a) -> a -> TestTree
108 chk title getActual expected =
128 chk title getActual expected =
109 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
111 innerCaps :: InitializeResponse -> InitializeResponseCapabilitiesInner
140 innerCaps :: InitializeResponse -> InitializeResponseCapabilitiesInner
112− innerCaps (ResponseMessage _ _ (Just (InitializeResponseCapabilities c)) _) = c
141+ innerCaps (ResponseMessage _ _ (Right (InitializeResponseCapabilities c))) = c
113 innerCaps _ = error "this test only expects inner capabilities"
142 innerCaps _ = error "this test only expects inner capabilities"
115 acquire :: IO InitializeResponse
144 acquire :: IO InitializeResponse
@@ -123,7 +152,7 @@ diagnosticTests :: TestTree
123diagnosticTests = testGroup "diagnostics"
152diagnosticTests = testGroup "diagnostics"
124 [ testSessionWait "fix syntax error" $ do
153 [ testSessionWait "fix syntax error" $ do
125 let content = T.unlines [ "module Testing wher" ]
154 let content = T.unlines [ "module Testing wher" ]
126− doc <- openDoc' "Testing.hs" "haskell" content
155+ doc <- createDoc "Testing.hs" "haskell" content
127 expectDiagnostics [("Testing.hs", [(DsError, (0, 15), "parse error")])]
156 expectDiagnostics [("Testing.hs", [(DsError, (0, 15), "parse error")])]
128 let change = TextDocumentContentChangeEvent
157 let change = TextDocumentContentChangeEvent
129 { _range = Just (Range (Position 0 15) (Position 0 19))
158 { _range = Just (Range (Position 0 15) (Position 0 19))
@@ -134,9 +163,9 @@ diagnosticTests = testGroup "diagnostics"
134 expectDiagnostics [("Testing.hs", [])]
163 expectDiagnostics [("Testing.hs", [])]
135 , testSessionWait "introduce syntax error" $ do
164 , testSessionWait "introduce syntax error" $ do
136 let content = T.unlines [ "module Testing where" ]
165 let content = T.unlines [ "module Testing where" ]
137− doc <- openDoc' "Testing.hs" "haskell" content
166+ doc <- createDoc "Testing.hs" "haskell" content
138− void (message :: Session WorkDoneProgressCreateRequest)
167+ void $ skipManyTill anyMessage (message :: Session WorkDoneProgressCreateRequest)
139− void (message :: Session WorkDoneProgressBeginNotification)
168+ void $ skipManyTill anyMessage (message :: Session WorkDoneProgressBeginNotification)
140 let change = TextDocumentContentChangeEvent
169 let change = TextDocumentContentChangeEvent
141 { _range = Just (Range (Position 0 15) (Position 0 18))
170 { _range = Just (Range (Position 0 15) (Position 0 18))
142 , _rangeLength = Nothing
171 , _rangeLength = Nothing
@@ -152,7 +181,7 @@ diagnosticTests = testGroup "diagnostics"
152 , "bar :: Int -> Int -> Int"
181 , "bar :: Int -> Int -> Int"
153 , "bar a b = cd + b"
182 , "bar a b = cd + b"
155− _ <- openDoc' "Testing.hs" "haskell" content
184+ _ <- createDoc "Testing.hs" "haskell" content
156 expectDiagnostics
185 expectDiagnostics
157 [ ( "Testing.hs"
186 [ ( "Testing.hs"
158 , [ (DsError, (2, 14), "Variable not in scope: ab")
187 , [ (DsError, (2, 14), "Variable not in scope: ab")
@@ -166,7 +195,7 @@ diagnosticTests = testGroup "diagnostics"
166 , "foo :: Int -> String -> Int"
195 , "foo :: Int -> String -> Int"
167 , "foo a b = a + b"
196 , "foo a b = a + b"
169− _ <- openDoc' "Testing.hs" "haskell" content
198+ _ <- createDoc "Testing.hs" "haskell" content
170 expectDiagnostics
199 expectDiagnostics
171 [ ( "Testing.hs"
200 [ ( "Testing.hs"
172 , [(DsError, (2, 14), "Couldn't match type '[Char]' with 'Int'")]
201 , [(DsError, (2, 14), "Couldn't match type '[Char]' with 'Int'")]
@@ -178,7 +207,7 @@ diagnosticTests = testGroup "diagnostics"
178 , "foo :: Int -> String"
207 , "foo :: Int -> String"
179 , "foo a = _ a"
208 , "foo a = _ a"
181− _ <- openDoc' "Testing.hs" "haskell" content
210+ _ <- createDoc "Testing.hs" "haskell" content
182 expectDiagnostics
211 expectDiagnostics
183 [ ( "Testing.hs"
212 [ ( "Testing.hs"
184 , [(DsError, (2, 8), "Found hole: _ :: Int -> String")]
213 , [(DsError, (2, 8), "Found hole: _ :: Int -> String")]
@@ -200,24 +229,23 @@ diagnosticTests = testGroup "diagnostics"
200 [ ("A.hs", [(DsError, (2,4), aMessage)])
229 [ ("A.hs", [(DsError, (2,4), aMessage)])
201 , ("B.hs", [(DsError, (3,4), bMessage)])]
230 , ("B.hs", [(DsError, (3,4), bMessage)])]
202 deferralTest title binding msg = testSessionWait title $ do
231 deferralTest title binding msg = testSessionWait title $ do
203− _ <- openDoc' "A.hs" "haskell" $ sourceA binding
232+ _ <- createDoc "A.hs" "haskell" $ sourceA binding
204− _ <- openDoc' "B.hs" "haskell" sourceB
233+ _ <- createDoc "B.hs" "haskell" sourceB
205 expectDiagnostics $ expectedDs msg
234 expectDiagnostics $ expectedDs msg
207 [ deferralTest "type error" "True" "Couldn't match expected type"
236 [ deferralTest "type error" "True" "Couldn't match expected type"
208 , deferralTest "typed hole" "_" "Found hole"
237 , deferralTest "typed hole" "_" "Found hole"
209 , deferralTest "out of scope var" "unbound" "Variable not in scope"
238 , deferralTest "out of scope var" "unbound" "Variable not in scope"
210− , deferralTest "message shows error" "True" "A.hs:3:5: error:"
213 , testSessionWait "remove required module" $ do
241 , testSessionWait "remove required module" $ do
214 let contentA = T.unlines [ "module ModuleA where" ]
242 let contentA = T.unlines [ "module ModuleA where" ]
215− docA <- openDoc' "ModuleA.hs" "haskell" contentA
243+ docA <- createDoc "ModuleA.hs" "haskell" contentA
216 let contentB = T.unlines
244 let contentB = T.unlines
217 [ "module ModuleB where"
245 [ "module ModuleB where"
218 , "import ModuleA"
246 , "import ModuleA"
220− _ <- openDoc' "ModuleB.hs" "haskell" contentB
248+ _ <- createDoc "ModuleB.hs" "haskell" contentB
221 let change = TextDocumentContentChangeEvent
249 let change = TextDocumentContentChangeEvent
222 { _range = Just (Range (Position 0 0) (Position 0 20))
250 { _range = Just (Range (Position 0 0) (Position 0 20))
223 , _rangeLength = Nothing
251 , _rangeLength = Nothing
@@ -230,11 +258,21 @@ diagnosticTests = testGroup "diagnostics"
230 [ "module ModuleB where"
258 [ "module ModuleB where"
231 , "import ModuleA"
259 , "import ModuleA"
233− _ <- openDoc' "ModuleB.hs" "haskell" contentB
261+ _ <- createDoc "ModuleB.hs" "haskell" contentB
234 expectDiagnostics [("ModuleB.hs", [(DsError, (1, 7), "Could not find module")])]
262 expectDiagnostics [("ModuleB.hs", [(DsError, (1, 7), "Could not find module")])]
235 let contentA = T.unlines [ "module ModuleA where" ]
263 let contentA = T.unlines [ "module ModuleA where" ]
236− _ <- openDoc' "ModuleA.hs" "haskell" contentA
264+ _ <- createDoc "ModuleA.hs" "haskell" contentA
237 expectDiagnostics [("ModuleB.hs", [])]
265 expectDiagnostics [("ModuleB.hs", [])]
266+ , testSessionWait "add missing module (non workspace)" $ do
267+ let contentB = T.unlines
268+ [ "module ModuleB where"
271+ _ <- createDoc "/tmp/ModuleB.hs" "haskell" contentB
272+ expectDiagnostics [("/tmp/ModuleB.hs", [(DsError, (1, 7), "Could not find module")])]
273+ let contentA = T.unlines [ "module ModuleA where" ]
274+ _ <- createDoc "/tmp/ModuleA.hs" "haskell" contentA
275+ expectDiagnostics [("/tmp/ModuleB.hs", [])]
238 , testSessionWait "cyclic module dependency" $ do
276 , testSessionWait "cyclic module dependency" $ do
239 let contentA = T.unlines
277 let contentA = T.unlines
240 [ "module ModuleA where"
278 [ "module ModuleA where"
@@ -244,8 +282,8 @@ diagnosticTests = testGroup "diagnostics"
244 [ "module ModuleB where"
282 [ "module ModuleB where"
245 , "import ModuleA"
283 , "import ModuleA"
247− _ <- openDoc' "ModuleA.hs" "haskell" contentA
285+ _ <- createDoc "ModuleA.hs" "haskell" contentA
248− _ <- openDoc' "ModuleB.hs" "haskell" contentB
286+ _ <- createDoc "ModuleB.hs" "haskell" contentB
249 expectDiagnostics
287 expectDiagnostics
250 [ ( "ModuleA.hs"
288 [ ( "ModuleA.hs"
251 , [(DsError, (1, 7), "Cyclic module dependency between ModuleA, ModuleB")]
289 , [(DsError, (1, 7), "Cyclic module dependency between ModuleA, ModuleB")]
@@ -266,9 +304,9 @@ diagnosticTests = testGroup "diagnostics"
266 let contentBboot = T.unlines
304 let contentBboot = T.unlines
267 [ "module ModuleB where"
305 [ "module ModuleB where"
269− _ <- openDoc' "ModuleA.hs" "haskell" contentA
307+ _ <- createDoc "ModuleA.hs" "haskell" contentA
270− _ <- openDoc' "ModuleB.hs" "haskell" contentB
308+ _ <- createDoc "ModuleB.hs" "haskell" contentB
271− _ <- openDoc' "ModuleB.hs-boot" "haskell" contentBboot
309+ _ <- createDoc "ModuleB.hs-boot" "haskell" contentBboot
272 expectDiagnostics []
310 expectDiagnostics []
273 , testSessionWait "correct reference used with hs-boot" $ do
311 , testSessionWait "correct reference used with hs-boot" $ do
274 let contentB = T.unlines
312 let contentB = T.unlines
@@ -290,10 +328,10 @@ diagnosticTests = testGroup "diagnostics"
290 -- resolved to the hs-boot file
328 -- resolved to the hs-boot file
291 , "y = x"
329 , "y = x"
293− _ <- openDoc' "ModuleB.hs" "haskell" contentB
331+ _ <- createDoc "ModuleB.hs" "haskell" contentB
294− _ <- openDoc' "ModuleA.hs" "haskell" contentA
332+ _ <- createDoc "ModuleA.hs" "haskell" contentA
295− _ <- openDoc' "ModuleA.hs-boot" "haskell" contentAboot
333+ _ <- createDoc "ModuleA.hs-boot" "haskell" contentAboot
296− _ <- openDoc' "ModuleC.hs" "haskell" contentC
334+ _ <- createDoc "ModuleC.hs" "haskell" contentC
297 expectDiagnostics []
335 expectDiagnostics []
298 , testSessionWait "redundant import" $ do
336 , testSessionWait "redundant import" $ do
299 let contentA = T.unlines ["module ModuleA where"]
337 let contentA = T.unlines ["module ModuleA where"]
@@ -302,8 +340,8 @@ diagnosticTests = testGroup "diagnostics"
302 , "module ModuleB where"
340 , "module ModuleB where"
303 , "import ModuleA"
341 , "import ModuleA"
305− _ <- openDoc' "ModuleA.hs" "haskell" contentA
343+ _ <- createDoc "ModuleA.hs" "haskell" contentA
306− _ <- openDoc' "ModuleB.hs" "haskell" contentB
344+ _ <- createDoc "ModuleB.hs" "haskell" contentB
307 expectDiagnostics
345 expectDiagnostics
308 [ ( "ModuleB.hs"
346 [ ( "ModuleB.hs"
309 , [(DsWarning, (2, 0), "The import of 'ModuleA' is redundant")]
347 , [(DsWarning, (2, 0), "The import of 'ModuleA' is redundant")]
@@ -325,8 +363,8 @@ diagnosticTests = testGroup "diagnostics"
325 , "wrong1 = ThisList.map"
363 , "wrong1 = ThisList.map"
326 , "wrong2 = BaseList.x"
364 , "wrong2 = BaseList.x"
328− _ <- openDoc' "Data/List.hs" "haskell" thisDataListContent
366+ _ <- createDoc "Data/List.hs" "haskell" thisDataListContent
329− _ <- openDoc' "Main.hs" "haskell" mainContent
367+ _ <- createDoc "Main.hs" "haskell" mainContent
330 expectDiagnostics
368 expectDiagnostics
331 [ ( "Main.hs"
369 [ ( "Main.hs"
332 , [(DsError, (6, 9), "Not in scope: \8216ThisList.map\8217")
370 , [(DsError, (6, 9), "Not in scope: \8216ThisList.map\8217")
@@ -341,7 +379,7 @@ diagnosticTests = testGroup "diagnostics"
341 , "foo :: Ord a => a -> Int"
379 , "foo :: Ord a => a -> Int"
342 , "foo a = 1"
380 , "foo a = 1"
344− _ <- openDoc' "Foo.hs" "haskell" fooContent
382+ _ <- createDoc "Foo.hs" "haskell" fooContent
345 expectDiagnostics
383 expectDiagnostics
346 [ ( "Foo.hs"
384 [ ( "Foo.hs"
347 -- The test is to make sure that warnings contain unqualified names
385 -- The test is to make sure that warnings contain unqualified names
@@ -366,18 +404,18 @@ diagnosticTests = testGroup "diagnostics"
366 Just pathB <- pure $ uriToFilePath uriB
404 Just pathB <- pure $ uriToFilePath uriB
367 uriB <- pure $
405 uriB <- pure $
368 let (drive, suffix) = splitDrive pathB
406 let (drive, suffix) = splitDrive pathB
369− in filePathToUri (joinDrive (map toLower drive ) suffix)
407+ in filePathToUri (joinDrive (lower drive) suffix)
370 liftIO $ createDirectoryIfMissing True (takeDirectory pathB)
408 liftIO $ createDirectoryIfMissing True (takeDirectory pathB)
371 liftIO $ writeFileUTF8 pathB $ T.unpack bContent
409 liftIO $ writeFileUTF8 pathB $ T.unpack bContent
372 uriA <- getDocUri "A/A.hs"
410 uriA <- getDocUri "A/A.hs"
373 Just pathA <- pure $ uriToFilePath uriA
411 Just pathA <- pure $ uriToFilePath uriA
374 uriA <- pure $
412 uriA <- pure $
375 let (drive, suffix) = splitDrive pathA
413 let (drive, suffix) = splitDrive pathA
376− in filePathToUri (joinDrive (map toLower drive ) suffix)
414+ in filePathToUri (joinDrive (lower drive) suffix)
377 let itemA = TextDocumentItem uriA "haskell" 0 aContent
415 let itemA = TextDocumentItem uriA "haskell" 0 aContent
378 let a = TextDocumentIdentifier uriA
416 let a = TextDocumentIdentifier uriA
379 sendNotification TextDocumentDidOpen (DidOpenTextDocumentParams itemA)
417 sendNotification TextDocumentDidOpen (DidOpenTextDocumentParams itemA)
380− diagsNot <- skipManyTill anyMessage message :: Session PublishDiagnosticsNotification
418+ diagsNot <- skipManyTill anyMessage diagnostic
381 let PublishDiagnosticsParams fileUri diags = _params (diagsNot :: PublishDiagnosticsNotification)
419 let PublishDiagnosticsParams fileUri diags = _params (diagsNot :: PublishDiagnosticsNotification)
382 -- Check that if we put a lower-case drive in for A.A
420 -- Check that if we put a lower-case drive in for A.A
383 -- the diagnostics for A.B will also be lower-case.
421 -- the diagnostics for A.B will also be lower-case.
@@ -386,6 +424,38 @@ diagnosticTests = testGroup "diagnostics"
386 liftIO $ unless ("redundant" `T.isInfixOf` msg) $
424 liftIO $ unless ("redundant" `T.isInfixOf` msg) $
387 assertFailure ("Expected redundant import but got " <> T.unpack msg)
425 assertFailure ("Expected redundant import but got " <> T.unpack msg)
388 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
391codeActionTests :: TestTree
461codeActionTests :: TestTree
@@ -394,6 +464,7 @@ codeActionTests = testGroup "code actions"
394 , typeWildCardActionTests
464 , typeWildCardActionTests
395 , removeImportTests
465 , removeImportTests
396 , extendImportTests
466 , extendImportTests
467+ , suggestImportTests
397 , addExtensionTests
468 , addExtensionTests
398 , fixConstructorImportTests
469 , fixConstructorImportTests
399 , importRenameActionTests
470 , importRenameActionTests
@@ -407,6 +478,33 @@ codeLensesTests = testGroup "code lenses"
407 [ addSigLensesTests
478 [ addSigLensesTests
481+watchedFilesTests :: TestTree
482+watchedFilesTests = testGroup "watched files"
483+ [ testSession' "workspace files" $ \sessionDir -> do
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
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
495+ , testSession' "non workspace file" $ \sessionDir -> do
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
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
505+ -- TODO add a test for didChangeWorkspaceFolder
410renameActionTests :: TestTree
508renameActionTests :: TestTree
411renameActionTests = testGroup "rename actions"
509renameActionTests = testGroup "rename actions"
412 [ testSession "change to local variable name" $ do
510 [ testSession "change to local variable name" $ do
@@ -415,7 +513,7 @@ renameActionTests = testGroup "rename actions"
415 , "foo :: Int -> Int"
513 , "foo :: Int -> Int"
416 , "foo argName = argNme"
514 , "foo argName = argNme"
418− doc <- openDoc' "Testing.hs" "haskell" content
516+ doc <- createDoc "Testing.hs" "haskell" content
419 _ <- waitForDiagnostics
517 _ <- waitForDiagnostics
420 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’"
421 executeCodeAction action
519 executeCodeAction action
@@ -433,7 +531,7 @@ renameActionTests = testGroup "rename actions"
433 , "foo :: Maybe a -> [a]"
531 , "foo :: Maybe a -> [a]"
434 , "foo = maybToList"
532 , "foo = maybToList"
436− doc <- openDoc' "Testing.hs" "haskell" content
534+ doc <- createDoc "Testing.hs" "haskell" content
437 _ <- waitForDiagnostics
535 _ <- waitForDiagnostics
438 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’"
439 executeCodeAction action
537 executeCodeAction action
@@ -451,7 +549,7 @@ renameActionTests = testGroup "rename actions"
451 , "foo :: Char -> Char -> Char -> Char"
549 , "foo :: Char -> Char -> Char -> Char"
452 , "foo argument1 argument2 argument3 = argumentX"
550 , "foo argument1 argument2 argument3 = argumentX"
454− doc <- openDoc' "Testing.hs" "haskell" content
552+ doc <- createDoc "Testing.hs" "haskell" content
455 _ <- waitForDiagnostics
553 _ <- waitForDiagnostics
456 _ <- findCodeActions doc (Range (Position 2 36) (Position 2 45))
554 _ <- findCodeActions doc (Range (Position 2 36) (Position 2 45))
457 ["Replace with ‘argument1’", "Replace with ‘argument2’", "Replace with ‘argument3’"]
555 ["Replace with ‘argument1’", "Replace with ‘argument2’", "Replace with ‘argument3’"]
@@ -463,7 +561,7 @@ renameActionTests = testGroup "rename actions"
463 , "monus x y = max 0 (x - y)"
561 , "monus x y = max 0 (x - y)"
464 , "foo x y = x `monnus` y"
562 , "foo x y = x `monnus` y"
466− doc <- openDoc' "Testing.hs" "haskell" content
564+ doc <- createDoc "Testing.hs" "haskell" content
467 _ <- waitForDiagnostics
565 _ <- waitForDiagnostics
468 actionsOrCommands <- getCodeActions doc (Range (Position 3 12) (Position 3 20))
566 actionsOrCommands <- getCodeActions doc (Range (Position 3 12) (Position 3 20))
469 [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 ]
@@ -486,7 +584,7 @@ typeWildCardActionTests = testGroup "type wildcard actions"
486 , "func :: _"
584 , "func :: _"
487 , "func x = x"
585 , "func x = x"
489− doc <- openDoc' "Testing.hs" "haskell" content
587+ doc <- createDoc "Testing.hs" "haskell" content
490 _ <- waitForDiagnostics
588 _ <- waitForDiagnostics
491 actionsOrCommands <- getCodeActions doc (Range (Position 2 1) (Position 2 10))
589 actionsOrCommands <- getCodeActions doc (Range (Position 2 1) (Position 2 10))
492 let [addSignature] = [action | CACodeAction action@CodeAction { _title = actionTitle } <- actionsOrCommands
590 let [addSignature] = [action | CACodeAction action@CodeAction { _title = actionTitle } <- actionsOrCommands
@@ -506,7 +604,7 @@ typeWildCardActionTests = testGroup "type wildcard actions"
506 , "func :: _"
604 , "func :: _"
507 , "func x y = x + y"
605 , "func x y = x + y"
509− doc <- openDoc' "Testing.hs" "haskell" content
607+ doc <- createDoc "Testing.hs" "haskell" content
510 _ <- waitForDiagnostics
608 _ <- waitForDiagnostics
511 actionsOrCommands <- getCodeActions doc (Range (Position 2 1) (Position 2 10))
609 actionsOrCommands <- getCodeActions doc (Range (Position 2 1) (Position 2 10))
512 let [addSignature] = [action | CACodeAction action@CodeAction { _title = actionTitle } <- actionsOrCommands
610 let [addSignature] = [action | CACodeAction action@CodeAction { _title = actionTitle } <- actionsOrCommands
@@ -529,7 +627,7 @@ typeWildCardActionTests = testGroup "type wildcard actions"
529 , " y = x * 2"
627 , " y = x * 2"
530 , " in y"
628 , " in y"
532− doc <- openDoc' "Testing.hs" "haskell" content
630+ doc <- createDoc "Testing.hs" "haskell" content
533 _ <- waitForDiagnostics
631 _ <- waitForDiagnostics
534 actionsOrCommands <- getCodeActions doc (Range (Position 4 1) (Position 4 10))
632 actionsOrCommands <- getCodeActions doc (Range (Position 4 1) (Position 4 10))
535 let [addSignature] = [action | CACodeAction action@CodeAction { _title = actionTitle } <- actionsOrCommands
633 let [addSignature] = [action | CACodeAction action@CodeAction { _title = actionTitle } <- actionsOrCommands
@@ -554,7 +652,7 @@ removeImportTests = testGroup "remove import actions"
554 let contentA = T.unlines
652 let contentA = T.unlines
555 [ "module ModuleA where"
653 [ "module ModuleA where"
557− _docA <- openDoc' "ModuleA.hs" "haskell" contentA
655+ _docA <- createDoc "ModuleA.hs" "haskell" contentA
558 let contentB = T.unlines
656 let contentB = T.unlines
559 [ "{-# OPTIONS_GHC -Wunused-imports #-}"
657 [ "{-# OPTIONS_GHC -Wunused-imports #-}"
560 , "module ModuleB where"
658 , "module ModuleB where"
@@ -562,7 +660,7 @@ removeImportTests = testGroup "remove import actions"
562 , "stuffB :: Integer"
660 , "stuffB :: Integer"
563 , "stuffB = 123"
661 , "stuffB = 123"
565− docB <- openDoc' "ModuleB.hs" "haskell" contentB
663+ docB <- createDoc "ModuleB.hs" "haskell" contentB
566 _ <- waitForDiagnostics
664 _ <- waitForDiagnostics
567 [CACodeAction action@CodeAction { _title = actionTitle }]
665 [CACodeAction action@CodeAction { _title = actionTitle }]
568 <- getCodeActions docB (Range (Position 2 0) (Position 2 5))
666 <- getCodeActions docB (Range (Position 2 0) (Position 2 5))
@@ -580,7 +678,7 @@ removeImportTests = testGroup "remove import actions"
580 let contentA = T.unlines
678 let contentA = T.unlines
581 [ "module ModuleA where"
679 [ "module ModuleA where"
583− _docA <- openDoc' "ModuleA.hs" "haskell" contentA
681+ _docA <- createDoc "ModuleA.hs" "haskell" contentA
584 let contentB = T.unlines
682 let contentB = T.unlines
585 [ "{-# OPTIONS_GHC -Wunused-imports #-}"
683 [ "{-# OPTIONS_GHC -Wunused-imports #-}"
586 , "module ModuleB where"
684 , "module ModuleB where"
@@ -588,7 +686,7 @@ removeImportTests = testGroup "remove import actions"
588 , "stuffB :: Integer"
686 , "stuffB :: Integer"
589 , "stuffB = 123"
687 , "stuffB = 123"
591− docB <- openDoc' "ModuleB.hs" "haskell" contentB
689+ docB <- createDoc "ModuleB.hs" "haskell" contentB
592 _ <- waitForDiagnostics
690 _ <- waitForDiagnostics
593 [CACodeAction action@CodeAction { _title = actionTitle }]
691 [CACodeAction action@CodeAction { _title = actionTitle }]
594 <- getCodeActions docB (Range (Position 2 0) (Position 2 5))
692 <- getCodeActions docB (Range (Position 2 0) (Position 2 5))
@@ -610,14 +708,14 @@ removeImportTests = testGroup "remove import actions"
610 , "stuffB = 123"
708 , "stuffB = 123"
611 , "stuffC = ()"
709 , "stuffC = ()"
613− _docA <- openDoc' "ModuleA.hs" "haskell" contentA
711+ _docA <- createDoc "ModuleA.hs" "haskell" contentA
614 let contentB = T.unlines
712 let contentB = T.unlines
615 [ "{-# OPTIONS_GHC -Wunused-imports #-}"
713 [ "{-# OPTIONS_GHC -Wunused-imports #-}"
616 , "module ModuleB where"
714 , "module ModuleB where"
617 , "import ModuleA (stuffA, stuffB, stuffC, stuffA)"
715 , "import ModuleA (stuffA, stuffB, stuffC, stuffA)"
618 , "main = print stuffB"
716 , "main = print stuffB"
620− docB <- openDoc' "ModuleB.hs" "haskell" contentB
718+ docB <- createDoc "ModuleB.hs" "haskell" contentB
621 _ <- waitForDiagnostics
719 _ <- waitForDiagnostics
622 [CACodeAction action@CodeAction { _title = actionTitle }]
720 [CACodeAction action@CodeAction { _title = actionTitle }]
623 <- getCodeActions docB (Range (Position 2 0) (Position 2 5))
721 <- getCodeActions docB (Range (Position 2 0) (Position 2 5))
@@ -631,29 +729,26 @@ removeImportTests = testGroup "remove import actions"
631 , "main = print stuffB"
729 , "main = print stuffB"
633 liftIO $ expectedContentAfterAction @=? contentAfterAction
731 liftIO $ expectedContentAfterAction @=? contentAfterAction
634− , testSession "redundant symbol binding" $ do
732+ , testSession "redundant operator" $ do
635 let contentA = T.unlines
733 let contentA = T.unlines
636 [ "module ModuleA where"
734 [ "module ModuleA where"
637 , "a !! b = a"
735 , "a !! b = a"
638 , "stuffB :: Integer"
737 , "stuffB :: Integer"
639 , "stuffB = 123"
738 , "stuffB = 123"
641− _docA <- openDoc' "ModuleA.hs" "haskell" contentA
740+ _docA <- createDoc "ModuleA.hs" "haskell" contentA
642 let contentB = T.unlines
741 let contentB = T.unlines
643 [ "{-# OPTIONS_GHC -Wunused-imports #-}"
742 [ "{-# OPTIONS_GHC -Wunused-imports #-}"
644 , "module ModuleB where"
743 , "module ModuleB where"
645− , "import qualified ModuleA as A ((!!), stuffB, (!!))"
744+ , "import qualified ModuleA as A ((<?>), stuffB, (!!))"
646 , "main = print A.stuffB"
745 , "main = print A.stuffB"
648− docB <- openDoc' "ModuleB.hs" "haskell" contentB
747+ docB <- createDoc "ModuleB.hs" "haskell" contentB
649 _ <- waitForDiagnostics
748 _ <- waitForDiagnostics
650 [CACodeAction action@CodeAction { _title = actionTitle }]
749 [CACodeAction action@CodeAction { _title = actionTitle }]
651 <- getCodeActions docB (Range (Position 2 0) (Position 2 5))
750 <- getCodeActions docB (Range (Position 2 0) (Position 2 5))
652−#if MIN_GHC_API_VERSION(8,6,0)
653− liftIO $ "Remove !! from import" @=? actionTitle
655− liftIO $ "Remove A.!! from import" @=? actionTitle
751+ liftIO $ "Remove !!, <?> from import" @=? actionTitle
657 executeCodeAction action
752 executeCodeAction action
658 contentAfterAction <- documentContents docB
753 contentAfterAction <- documentContents docB
659 let expectedContentAfterAction = T.unlines
754 let expectedContentAfterAction = T.unlines
@@ -663,21 +758,21 @@ removeImportTests = testGroup "remove import actions"
663 , "main = print A.stuffB"
758 , "main = print A.stuffB"
665 liftIO $ expectedContentAfterAction @=? contentAfterAction
760 liftIO $ expectedContentAfterAction @=? contentAfterAction
666− , (`xfail` "known broken (#299)") $ testSession "redundant hierarchical import" $ do
761+ , testSession "redundant all import" $ do
667 let contentA = T.unlines
762 let contentA = T.unlines
668 [ "module ModuleA where"
763 [ "module ModuleA where"
669 , "data A = A"
764 , "data A = A"
670 , "stuffB :: Integer"
765 , "stuffB :: Integer"
671 , "stuffB = 123"
766 , "stuffB = 123"
673− _docA <- openDoc' "ModuleA.hs" "haskell" contentA
768+ _docA <- createDoc "ModuleA.hs" "haskell" contentA
674 let contentB = T.unlines
769 let contentB = T.unlines
675 [ "{-# OPTIONS_GHC -Wunused-imports #-}"
770 [ "{-# OPTIONS_GHC -Wunused-imports #-}"
676 , "module ModuleB where"
771 , "module ModuleB where"
677 , "import ModuleA (A(..), stuffB)"
772 , "import ModuleA (A(..), stuffB)"
678 , "main = print stuffB"
773 , "main = print stuffB"
680− docB <- openDoc' "ModuleB.hs" "haskell" contentB
775+ docB <- createDoc "ModuleB.hs" "haskell" contentB
681 _ <- waitForDiagnostics
776 _ <- waitForDiagnostics
682 [CACodeAction action@CodeAction { _title = actionTitle }]
777 [CACodeAction action@CodeAction { _title = actionTitle }]
683 <- getCodeActions docB (Range (Position 2 0) (Position 2 5))
778 <- getCodeActions docB (Range (Position 2 0) (Position 2 5))
@@ -691,6 +786,33 @@ removeImportTests = testGroup "remove import actions"
691 , "main = print stuffB"
786 , "main = print stuffB"
693 liftIO $ expectedContentAfterAction @=? contentAfterAction
788 liftIO $ expectedContentAfterAction @=? contentAfterAction
789+ , testSession "redundant constructor import" $ do
790+ let contentA = T.unlines
791+ [ "module ModuleA where"
795+ _docA <- createDoc "ModuleA.hs" "haskell" contentA
796+ let contentB = T.unlines
797+ [ "{-# OPTIONS_GHC -Wunused-imports #-}"
798+ , "module ModuleB where"
799+ , "import ModuleA (D(A,B), E(F))"
802+ docB <- createDoc "ModuleB.hs" "haskell" contentB
803+ _ <- waitForDiagnostics
804+ [CACodeAction action@CodeAction { _title = actionTitle }]
805+ <- getCodeActions docB (Range (Position 2 0) (Position 2 5))
806+ liftIO $ "Remove A, E, F from import" @=? actionTitle
807+ executeCodeAction action
808+ contentAfterAction <- documentContents docB
809+ let expectedContentAfterAction = T.unlines
810+ [ "{-# OPTIONS_GHC -Wunused-imports #-}"
811+ , "module ModuleB where"
812+ , "import ModuleA (D(B))"
815+ liftIO $ expectedContentAfterAction @=? contentAfterAction
696extendImportTests :: TestTree
818extendImportTests :: TestTree
@@ -715,6 +837,26 @@ extendImportTests = testGroup "extend import actions"
715 , "import ModuleA as A (stuffA, stuffB)"
837 , "import ModuleA as A (stuffA, stuffB)"
716 , "main = print (stuffA, stuffB)"
838 , "main = print (stuffA, stuffB)"
840+ , testSession "extend single line import with operator" $ template
842+ [ "module ModuleA where"
843+ , "(.*) :: Integer -> Integer -> Integer"
845+ , "stuffB :: Integer"
849+ [ "module ModuleB where"
850+ , "import ModuleA as A (stuffB)"
851+ , "main = print (stuffB .* stuffB)"
853+ (Range (Position 3 17) (Position 3 18))
854+ "Add .* to the import list of ModuleA"
856+ [ "module ModuleB where"
857+ , "import ModuleA as A ((.*), stuffB)"
858+ , "main = print (stuffB .* stuffB)"
718 , testSession "extend single line import with type" $ template
860 , testSession "extend single line import with type" $ template
719 (T.unlines
861 (T.unlines
720 [ "module ModuleA where"
862 [ "module ModuleA where"
@@ -798,8 +940,8 @@ extendImportTests = testGroup "extend import actions"
800 template contentA contentB range expectedAction expectedContentB = do
942 template contentA contentB range expectedAction expectedContentB = do
801− _docA <- openDoc' "ModuleA.hs" "haskell" contentA
943+ _docA <- createDoc "ModuleA.hs" "haskell" contentA
802− docB <- openDoc' "ModuleB.hs" "haskell" contentB
944+ docB <- createDoc "ModuleB.hs" "haskell" contentB
803 _ <- waitForDiagnostics
945 _ <- waitForDiagnostics
804 CACodeAction action@CodeAction { _title = actionTitle } : _
946 CACodeAction action@CodeAction { _title = actionTitle } : _
805 <- sortOn (\(CACodeAction CodeAction{_title=x}) -> x) <$>
947 <- sortOn (\(CACodeAction CodeAction{_title=x}) -> x) <$>
@@ -809,6 +951,61 @@ extendImportTests = testGroup "extend import actions"
809 contentAfterAction <- documentContents docB
951 contentAfterAction <- documentContents docB
810 liftIO $ expectedContentB @=? contentAfterAction
952 liftIO $ expectedContentB @=? contentAfterAction
954+suggestImportTests :: TestTree
955+suggestImportTests = testGroup "suggest import actions"
956+ [ testGroup "Dont want suggestion"
958+ test False ["Data.List.NonEmpty ()"] "f = nonEmpty" [] "import Data.List.NonEmpty (nonEmpty)"
960+ , test False [] "f = First" [] "import Data.Monoid (First)"
962+ , test False [] "f :: Typeable a => a" ["f = undefined"] "import Data.Typeable.Internal (Typeable)"
963+ -- package not in scope
964+ , test False [] "f = quickCheck" [] "import Test.QuickCheck (quickCheck)"
966+ , testGroup "want suggestion"
967+ [ test True [] "f = nonEmpty" [] "import Data.List.NonEmpty (nonEmpty)"
968+ , test True [] "f = (:|)" [] "import Data.List.NonEmpty (NonEmpty((:|)))"
969+ , test True [] "f :: Natural" ["f = undefined"] "import Numeric.Natural (Natural)"
970+ , test True [] "f :: NonEmpty ()" ["f = () :| []"] "import Data.List.NonEmpty (NonEmpty)"
971+ , test True [] "f = First" [] "import Data.Monoid (First(First))"
972+ , test True [] "f = Endo" [] "import Data.Monoid (Endo(Endo))"
973+ , test True [] "f = Version" [] "import Data.Version (Version(Version))"
974+ , test True [] "f ExitSuccess = ()" [] "import System.Exit (ExitCode(ExitSuccess))"
975+ , test True [] "f = AssertionFailed" [] "import Control.Exception (AssertionFailed(AssertionFailed))"
976+ , test True ["Prelude"] "f = nonEmpty" [] "import Data.List.NonEmpty (nonEmpty)"
977+ , test True [] "f :: Alternative f => f ()" ["f = undefined"] "import Control.Applicative (Alternative)"
978+ , test True [] "f = empty" [] "import Control.Applicative (Alternative(empty))"
979+ , test True [] "f = (&)" [] "import Data.Function ((&))"
980+ , test True [] "f = NE.nonEmpty" [] "import qualified Data.List.NonEmpty as NE"
981+ , test True [] "f :: Typeable a => a" ["f = undefined"] "import Data.Typeable (Typeable)"
982+ , test True [] "f = pack" [] "import Data.Text (pack)"
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 ((&))"
989+ test wanted imps def other newImp = testSession' (T.unpack def) $ \dir -> do
990+ let before = T.unlines $ "module A where" : ["import " <> x | x <- imps] ++ def : other
991+ after = T.unlines $ "module A where" : ["import " <> x | x <- imps] ++ [newImp] ++ def : other
992+ cradle = "cradle: {direct: {arguments: [-hide-all-packages, -package, base, -package, text, -package-env, -]}}"
993+ liftIO $ writeFileUTF8 (dir </> "hie.yaml") cradle
994+ doc <- createDoc "Test.hs" "haskell" before
995+ _diags <- waitForDiagnostics
996+ let defLine = length imps + 1
997+ range = Range (Position defLine 0) (Position defLine maxBound)
998+ actions <- getCodeActions doc range
1001+ action <- liftIO $ pickActionWithTitle newImp actions
1002+ executeCodeAction action
1003+ contentAfterAction <- documentContents doc
1004+ liftIO $ after @=? contentAfterAction
1006+ liftIO $ [_title | CACodeAction CodeAction{_title} <- actions, _title == newImp ] @?= []
812addExtensionTests :: TestTree
1009addExtensionTests :: TestTree
813addExtensionTests = testGroup "add language extension actions"
1010addExtensionTests = testGroup "add language extension actions"
814 [ testSession "add NamedFieldPuns language extension" $ template
1011 [ testSession "add NamedFieldPuns language extension" $ template
@@ -854,7 +1051,7 @@ addExtensionTests = testGroup "add language extension actions"
856 template initialContent range expectedAction expectedContents = do
1053 template initialContent range expectedAction expectedContents = do
857− doc <- openDoc' "Module.hs" "haskell" initialContent
1054+ doc <- createDoc "Module.hs" "haskell" initialContent
858 _ <- waitForDiagnostics
1055 _ <- waitForDiagnostics
859 CACodeAction action@CodeAction { _title = actionTitle } : _
1056 CACodeAction action@CodeAction { _title = actionTitle } : _
860 <- sortOn (\(CACodeAction CodeAction{_title=x}) -> x) <$>
1057 <- sortOn (\(CACodeAction CodeAction{_title=x}) -> x) <$>
@@ -877,7 +1074,7 @@ insertNewDefinitionTests = testGroup "insert new definition actions"
878 ,"someOtherCode = ()"
1075 ,"someOtherCode = ()"
880− docB <- openDoc' "ModuleB.hs" "haskell" (T.unlines $ txtB ++ txtB')
1077+ docB <- createDoc "ModuleB.hs" "haskell" (T.unlines $ txtB ++ txtB')
881 _ <- waitForDiagnostics
1078 _ <- waitForDiagnostics
882 CACodeAction action@CodeAction { _title = actionTitle } : _
1079 CACodeAction action@CodeAction { _title = actionTitle } : _
883 <- sortOn (\(CACodeAction CodeAction{_title=x}) -> x) <$>
1080 <- sortOn (\(CACodeAction CodeAction{_title=x}) -> x) <$>
@@ -901,7 +1098,7 @@ insertNewDefinitionTests = testGroup "insert new definition actions"
902 ,"someOtherCode = ()"
1099 ,"someOtherCode = ()"
904− docB <- openDoc' "ModuleB.hs" "haskell" (T.unlines $ txtB ++ txtB')
1101+ docB <- createDoc "ModuleB.hs" "haskell" (T.unlines $ txtB ++ txtB')
905 _ <- waitForDiagnostics
1102 _ <- waitForDiagnostics
906 CACodeAction action@CodeAction { _title = actionTitle } : _
1103 CACodeAction action@CodeAction { _title = actionTitle } : _
907 <- sortOn (\(CACodeAction CodeAction{_title=x}) -> x) <$>
1104 <- sortOn (\(CACodeAction CodeAction{_title=x}) -> x) <$>
@@ -940,8 +1137,8 @@ fixConstructorImportTests = testGroup "fix import actions"
942 template contentA contentB range expectedAction expectedContentB = do
1139 template contentA contentB range expectedAction expectedContentB = do
943− _docA <- openDoc' "ModuleA.hs" "haskell" contentA
1140+ _docA <- createDoc "ModuleA.hs" "haskell" contentA
944− docB <- openDoc' "ModuleB.hs" "haskell" contentB
1141+ docB <- createDoc "ModuleB.hs" "haskell" contentB
945 _diags <- waitForDiagnostics
1142 _diags <- waitForDiagnostics
946 CACodeAction action@CodeAction { _title = actionTitle } : _
1143 CACodeAction action@CodeAction { _title = actionTitle } : _
947 <- sortOn (\(CACodeAction CodeAction{_title=x}) -> x) <$>
1144 <- sortOn (\(CACodeAction CodeAction{_title=x}) -> x) <$>
@@ -960,7 +1157,7 @@ importRenameActionTests = testGroup "import rename actions"
960 [ "module Testing where"
1157 [ "module Testing where"
961 , "import Data.Mape"
1158 , "import Data.Mape"
963− doc <- openDoc' "Testing.hs" "haskell" content
1160+ doc <- createDoc "Testing.hs" "haskell" content
964 _ <- waitForDiagnostics
1161 _ <- waitForDiagnostics
965 actionsOrCommands <- getCodeActions doc (Range (Position 2 8) (Position 2 16))
1162 actionsOrCommands <- getCodeActions doc (Range (Position 2 8) (Position 2 16))
966 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 ]
@@ -997,10 +1194,10 @@ fillTypedHoleTests = let
997 newA newB newC = testSession (T.unpack actionTitle) $ do
1194 newA newB newC = testSession (T.unpack actionTitle) $ do
998 let originalCode = sourceCode oldA oldB oldC
1195 let originalCode = sourceCode oldA oldB oldC
999 let expectedCode = sourceCode newA newB newC
1196 let expectedCode = sourceCode newA newB newC
1000− doc <- openDoc' "Testing.hs" "haskell" originalCode
1197+ doc <- createDoc "Testing.hs" "haskell" originalCode
1001 _ <- waitForDiagnostics
1198 _ <- waitForDiagnostics
1002 actionsOrCommands <- getCodeActions doc (Range (Position 9 0) (Position 9 maxBound))
1199 actionsOrCommands <- getCodeActions doc (Range (Position 9 0) (Position 9 maxBound))
1003− let chosenAction = pickActionWithTitle actionTitle actionsOrCommands
1200+ chosenAction <- liftIO $ pickActionWithTitle actionTitle actionsOrCommands
1004 executeCodeAction chosenAction
1201 executeCodeAction chosenAction
1005 modifiedCode <- documentContents doc
1202 modifiedCode <- documentContents doc
1006 liftIO $ expectedCode @=? modifiedCode
1203 liftIO $ expectedCode @=? modifiedCode
@@ -1037,36 +1234,37 @@ fillTypedHoleTests = let
1038addSigActionTests :: TestTree
1235addSigActionTests :: TestTree
1039addSigActionTests = let
1236addSigActionTests = let
1040− header = "{-# OPTIONS_GHC -Wmissing-signatures #-}"
1237+ header = "{-# OPTIONS_GHC -Wmissing-signatures -Wmissing-pattern-synonym-signatures #-}"
1041− moduleH = "module Sigs where"
1238+ moduleH = "{-# LANGUAGE PatternSynonyms #-}\nmodule Sigs where"
1042 before def = T.unlines [header, moduleH, def]
1239 before def = T.unlines [header, moduleH, def]
1043 after' def sig = T.unlines [header, moduleH, sig, def]
1240 after' def sig = T.unlines [header, moduleH, sig, def]
1045 def >:: sig = testSession (T.unpack def) $ do
1242 def >:: sig = testSession (T.unpack def) $ do
1046 let originalCode = before def
1243 let originalCode = before def
1047 let expectedCode = after' def sig
1244 let expectedCode = after' def sig
1048− doc <- openDoc' "Sigs.hs" "haskell" originalCode
1245+ doc <- createDoc "Sigs.hs" "haskell" originalCode
1049 _ <- waitForDiagnostics
1246 _ <- waitForDiagnostics
1050 actionsOrCommands <- getCodeActions doc (Range (Position 3 1) (Position 3 maxBound))
1247 actionsOrCommands <- getCodeActions doc (Range (Position 3 1) (Position 3 maxBound))
1051− let chosenAction = pickActionWithTitle ("add signature: " <> sig) actionsOrCommands
1248+ chosenAction <- liftIO $ pickActionWithTitle ("add signature: " <> sig) actionsOrCommands
1052 executeCodeAction chosenAction
1249 executeCodeAction chosenAction
1053 modifiedCode <- documentContents doc
1250 modifiedCode <- documentContents doc
1054 liftIO $ expectedCode @=? modifiedCode
1251 liftIO $ expectedCode @=? modifiedCode
1056 testGroup "add signature"
1253 testGroup "add signature"
1057− [ "abc = True" >:: "abc :: Bool"
1254+ [ "abc = True" >:: "abc :: Bool"
1058− , "foo a b = a + b" >:: "foo :: Num a => a -> a -> a"
1255+ , "foo a b = a + b" >:: "foo :: Num a => a -> a -> a"
1059− , "bar a b = show $ a + b" >:: "bar :: (Show a, Num a) => a -> a -> String"
1256+ , "bar a b = show $ a + b" >:: "bar :: (Show a, Num a) => a -> a -> String"
1060− , "(!!!) a b = a > b" >:: "(!!!) :: Ord a => a -> a -> Bool"
1257+ , "(!!!) a b = a > b" >:: "(!!!) :: Ord a => a -> a -> Bool"
1061− , "a >>>> b = a + b" >:: "(>>>>) :: Num a => a -> a -> a"
1258+ , "a >>>> b = a + b" >:: "(>>>>) :: Num a => a -> a -> a"
1062− , "a `haha` b = a b" >:: "haha :: (t1 -> t2) -> t1 -> t2"
1259+ , "a `haha` b = a b" >:: "haha :: (t1 -> t2) -> t1 -> t2"
1260+ , "pattern Some a = Just a" >:: "pattern Some :: a -> Maybe a"
1065addSigLensesTests :: TestTree
1263addSigLensesTests :: TestTree
1066addSigLensesTests = let
1264addSigLensesTests = let
1067− missing = "{-# OPTIONS_GHC -Wmissing-signatures -Wunused-matches #-}"
1265+ missing = "{-# OPTIONS_GHC -Wmissing-signatures -Wmissing-pattern-synonym-signatures -Wunused-matches #-}"
1068 notMissing = "{-# OPTIONS_GHC -Wunused-matches #-}"
1266 notMissing = "{-# OPTIONS_GHC -Wunused-matches #-}"
1069− moduleH = "module Sigs where"
1267+ moduleH = "{-# LANGUAGE PatternSynonyms #-}\nmodule Sigs where"
1070 other = T.unlines ["f :: Integer -> Integer", "f x = 3"]
1268 other = T.unlines ["f :: Integer -> Integer", "f x = 3"]
1071 before withMissing def
1269 before withMissing def
1072 = T.unlines $ (if withMissing then (missing :) else (notMissing :)) [moduleH, def, other]
1270 = T.unlines $ (if withMissing then (missing :) else (notMissing :)) [moduleH, def, other]
@@ -1076,49 +1274,41 @@ addSigLensesTests = let
1076 sigSession withMissing def sig = testSession (T.unpack def) $ do
1274 sigSession withMissing def sig = testSession (T.unpack def) $ do
1077 let originalCode = before withMissing def
1275 let originalCode = before withMissing def
1078 let expectedCode = after' withMissing def sig
1276 let expectedCode = after' withMissing def sig
1079− doc <- openDoc' "Sigs.hs" "haskell" originalCode
1277+ doc <- createDoc "Sigs.hs" "haskell" originalCode
1080 [CodeLens {_command = Just c}] <- getCodeLenses doc
1278 [CodeLens {_command = Just c}] <- getCodeLenses doc
1081 executeCommand c
1279 executeCommand c
1082 modifiedCode <- getDocumentEdit doc
1280 modifiedCode <- getDocumentEdit doc
1083 liftIO $ expectedCode @=? modifiedCode
1281 liftIO $ expectedCode @=? modifiedCode
1085 testGroup "add signature"
1283 testGroup "add signature"
1086− [ testGroup "with warnings enabled"
1284+ [ testGroup title
1087− [ sigSession True "abc = True" "abc :: Bool"
1285+ [ sigSession enableWarnings "abc = True" "abc :: Bool"
1088− , sigSession True "foo a b = a + b" "foo :: Num a => a -> a -> a"
1286+ , sigSession enableWarnings "foo a b = a + b" "foo :: Num a => a -> a -> a"
1089− , sigSession True "bar a b = show $ a + b" "bar :: (Show a, Num a) => a -> a -> String"
1287+ , sigSession enableWarnings "bar a b = show $ a + b" "bar :: (Show a, Num a) => a -> a -> String"
1090− , sigSession True "(!!!) a b = a > b" "(!!!) :: Ord a => a -> a -> Bool"
1288+ , sigSession enableWarnings "(!!!) a b = a > b" "(!!!) :: Ord a => a -> a -> Bool"
1091− , sigSession True "a >>>> b = a + b" "(>>>>) :: Num a => a -> a -> a"
1289+ , sigSession enableWarnings "a >>>> b = a + b" "(>>>>) :: Num a => a -> a -> a"
1092− , sigSession True "a `haha` b = a b" "haha :: (t1 -> t2) -> t1 -> t2"
1290+ , sigSession enableWarnings "a `haha` b = a b" "haha :: (t1 -> t2) -> t1 -> t2"
1093− ]
1291+ , sigSession enableWarnings "pattern Some a = Just a" "pattern Some :: a -> Maybe a"
1094− , testGroup "with warnings disabled"
1095− [ sigSession False "abc = True" "abc :: Bool"
1096− , sigSession False "foo a b = a + b" "foo :: Num a => a -> a -> a"
1097− , sigSession False "bar a b = show $ a + b" "bar :: (Show a, Num a) => a -> a -> String"
1098− , sigSession False "(!!!) a b = a > b" "(!!!) :: Ord a => a -> a -> Bool"
1099− , sigSession False "a >>>> b = a + b" "(>>>>) :: Num a => a -> a -> a"
1100− , sigSession False "a `haha` b = a b" "haha :: (t1 -> t2) -> t1 -> t2"
1293+ | (title, enableWarnings) <-
1294+ [("with warnings enabled", True)
1295+ ,("with warnings disabled", False)
1104−findDefinitionAndHoverTests :: TestTree
1105−findDefinitionAndHoverTests = let
1107− tst (get, check) pos targetRange title = testSession title $ do
1108− doc <- openTestDataDoc sourceFilePath
1109− found <- get doc pos
1110− check found targetRange
1112− checkDefs :: [Location] -> [Expect] -> Session ()
1299+checkDefs :: [Location] -> Session [Expect] -> Session ()
1113− checkDefs defs expectations = traverse_ check expectations where
1300+checkDefs defs mkExpectations = traverse_ check =<< mkExpectations where
1115− check (ExpectRange expectedRange) = do
1302+ check (ExpectRange expectedRange) = do
1116− assertNDefinitionsFound 1 defs
1303+ assertNDefinitionsFound 1 defs
1117− assertRangeCorrect (head defs) expectedRange
1304+ assertRangeCorrect (head defs) expectedRange
1305+ check (ExpectLocation expectedLocation) = do
1306+ assertNDefinitionsFound 1 defs
1307+ liftIO $ head defs @?= expectedLocation
1118− check ExpectNoDefinitions = do
1308+ check ExpectNoDefinitions = do
1119− assertNDefinitionsFound 0 defs
1309+ assertNDefinitionsFound 0 defs
1120− 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"
1121− check _ = pure () -- all other expectations not relevant to getDefinition
1311+ check _ = pure () -- all other expectations not relevant to getDefinition
1123 assertNDefinitionsFound :: Int -> [a] -> Session ()
1313 assertNDefinitionsFound :: Int -> [a] -> Session ()
1124 assertNDefinitionsFound n defs = liftIO $ assertEqual "number of definitions" n (length defs)
1314 assertNDefinitionsFound n defs = liftIO $ assertEqual "number of definitions" n (length defs)
@@ -1126,8 +1316,19 @@ findDefinitionAndHoverTests = let
1126 assertRangeCorrect Location{_range = foundRange} expectedRange =
1316 assertRangeCorrect Location{_range = foundRange} expectedRange =
1127 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
1129− checkHover :: Maybe Hover -> [Expect] -> Session ()
1330+ checkHover :: Maybe Hover -> Session [Expect] -> Session ()
1130− checkHover hover expectations = traverse_ check expectations where
1331+ checkHover hover expectations = traverse_ check =<< expectations where
1132 check expected =
1333 check expected =
1133 case hover of
1334 case hover of
@@ -1170,9 +1371,12 @@ findDefinitionAndHoverTests = let
1171 mkFindTests tests = testGroup "get"
1372 mkFindTests tests = testGroup "get"
1172 [ testGroup "definition" $ mapMaybe fst tests
1373 [ testGroup "definition" $ mapMaybe fst tests
1173− , testGroup "hover" $ mapMaybe snd tests ]
1374+ , testGroup "hover" $ mapMaybe snd tests
1375+ , checkFileCompiles sourceFilePath ]
1175− test runDef runHover look expect title =
1377+ test runDef runHover look expect = testM runDef runHover look (return expect)
1379+ testM runDef runHover look expect title =
1176 ( runDef $ tst def look expect title
1380 ( runDef $ tst def look expect title
1177 , runHover $ tst hover look expect title ) where
1381 , runHover $ tst hover look expect title ) where
1178 def = (getDefinitions, checkDefs)
1382 def = (getDefinitions, checkDefs)
@@ -1180,87 +1384,100 @@ findDefinitionAndHoverTests = let
1180 --type_ = (getTypeDefinitions, checkTDefs) -- getTypeDefinitions always times out
1384 --type_ = (getTypeDefinitions, checkTDefs) -- getTypeDefinitions always times out
1182 -- search locations expectations on results
1386 -- search locations expectations on results
1183− fffL4 = _start fffR ; fffR = mkRange 4 4 4 7 ; fff = [ExpectRange fffR]
1387+ fffL4 = _start fffR ; fffR = mkRange 8 4 8 7 ; fff = [ExpectRange fffR]
1184− fffL8 = Position 8 4 ;
1388+ fffL8 = Position 12 4 ;
1185− fffL14 = Position 14 7 ;
1389+ fffL14 = Position 18 7 ;
1186− aaaL14 = Position 14 20 ; aaa = [mkR 7 0 7 3]
1390+ aaaL14 = Position 18 20 ; aaa = [mkR 11 0 11 3]
1187− dcL7 = Position 7 11 ; tcDC = [mkR 3 23 5 16]
1391+ dcL7 = Position 11 11 ; tcDC = [mkR 7 23 9 16]
1188− dcL12 = Position 12 11 ;
1392+ dcL12 = Position 16 11 ;
1189− xtcL5 = Position 5 11 ; xtc = [ExpectExternFail, ExpectHoverText ["Int", "Defined in ‘GHC.Types’"]]
1393+ xtcL5 = Position 9 11 ; xtc = [ExpectExternFail, ExpectHoverText ["Int", "Defined in ‘GHC.Types’"]]
1190− tcL6 = Position 6 11 ; tcData = [mkR 3 0 5 16, ExpectHoverText ["TypeConstructor", "GotoHover.hs:4:1"]]
1394+ tcL6 = Position 10 11 ; tcData = [mkR 7 0 9 16, ExpectHoverText ["TypeConstructor", "GotoHover.hs:8:1"]]
1191− vvL16 = Position 16 12 ; vv = [mkR 16 4 16 6]
1395+ vvL16 = Position 20 12 ; vv = [mkR 20 4 20 6]
1192− opL16 = Position 16 15 ; op = [mkR 17 2 17 4]
1396+ opL16 = Position 20 15 ; op = [mkR 21 2 21 4]
1193− opL18 = Position 18 22 ; opp = [mkR 18 13 18 17]
1397+ opL18 = Position 22 22 ; opp = [mkR 22 13 22 17]
1194− aL18 = Position 18 20 ; apmp = [mkR 18 10 18 11]
1398+ aL18 = Position 22 20 ; apmp = [mkR 22 10 22 11]
1195− b'L19 = Position 19 13 ; bp = [mkR 19 6 19 7]
1399+ b'L19 = Position 23 13 ; bp = [mkR 23 6 23 7]
1196− xvL20 = Position 20 8 ; xvMsg = [ExpectExternFail, ExpectHoverText ["Data.Text.pack", ":: String -> Text"]]
1400+ xvL20 = Position 24 8 ; xvMsg = [ExpectExternFail, ExpectHoverText ["Data.Text.pack", ":: String -> Text"]]
1197− clL23 = Position 23 11 ; cls = [mkR 21 0 22 20, ExpectHoverText ["MyClass", "GotoHover.hs:22:1"]]
1401+ clL23 = Position 27 11 ; cls = [mkR 25 0 26 20, ExpectHoverText ["MyClass", "GotoHover.hs:26:1"]]
1198− clL25 = Position 25 9
1402+ clL25 = Position 29 9
1199− eclL15 = Position 15 8 ; ecls = [ExpectExternFail, ExpectHoverText ["Num", "Defined in ‘GHC.Num’"]]
1403+ eclL15 = Position 19 8 ; ecls = [ExpectExternFail, ExpectHoverText ["Num", "Defined in ‘GHC.Num’"]]
1200− dnbL29 = Position 29 18 ; dnb = [ExpectHoverText [":: ()"], mkR 29 12 29 21]
1404+ dnbL29 = Position 33 18 ; dnb = [ExpectHoverText [":: ()"], mkR 33 12 33 21]
1201− dnbL30 = Position 30 23
1405+ dnbL30 = Position 34 23
1202− lcbL33 = Position 33 26 ; lcb = [ExpectHoverText [":: Char"], mkR 33 26 33 27]
1406+ lcbL33 = Position 37 26 ; lcb = [ExpectHoverText [":: Char"], mkR 37 26 37 27]
1203− lclL33 = Position 33 22
1407+ lclL33 = Position 37 22
1204− mclL36 = Position 36 1 ; mcl = [mkR 36 0 36 14]
1408+ mclL36 = Position 40 1 ; mcl = [mkR 40 0 40 14]
1205− mclL37 = Position 37 1
1409+ mclL37 = Position 41 1
1206− spaceL37 = Position 37 24 ; space = [ExpectNoDefinitions, ExpectHoverText [":: Char"]]
1410+ spaceL37 = Position 41 24 ; space = [ExpectNoDefinitions, ExpectHoverText [":: Char"]]
1207− docL41 = Position 41 1 ; doc = [ExpectHoverText ["Recognizable docs: kpqz"]]
1411+ docL41 = Position 45 1 ; doc = [ExpectHoverText ["Recognizable docs: kpqz"]]
1208 ; constr = [ExpectHoverText ["Monad m"]]
1412 ; constr = [ExpectHoverText ["Monad m"]]
1209− eitL40 = Position 40 28 ; kindE = [ExpectHoverText [":: * -> * -> *\n"]]
1413+ eitL40 = Position 44 28 ; kindE = [ExpectHoverText [":: * -> * -> *\n"]]
1210− intL40 = Position 40 34 ; kindI = [ExpectHoverText [":: *\n"]]
1414+ intL40 = Position 44 34 ; kindI = [ExpectHoverText [":: *\n"]]
1211− tvrL40 = Position 40 37 ; kindV = [ExpectHoverText [":: * -> *\n"]]
1415+ tvrL40 = Position 44 37 ; kindV = [ExpectHoverText [":: * -> *\n"]]
1212− intL41 = Position 41 20 ; litI = [ExpectHoverText ["7518"]]
1416+ intL41 = Position 45 20 ; litI = [ExpectHoverText ["7518"]]
1213− chrL36 = Position 37 24 ; litC = [ExpectHoverText ["'f'"]]
1417+ chrL36 = Position 41 24 ; litC = [ExpectHoverText ["'f'"]]
1214− txtL8 = Position 8 14 ; litT = [ExpectHoverText ["\"dfgy\""]]
1418+ txtL8 = Position 12 14 ; litT = [ExpectHoverText ["\"dfgy\""]]
1215− lstL43 = Position 43 12 ; litL = [ExpectHoverText ["[8391 :: Int, 6268]"]]
1419+ lstL43 = Position 47 12 ; litL = [ExpectHoverText ["[8391 :: Int, 6268]"]]
1216− outL45 = Position 45 3 ; outSig = [ExpectHoverText ["outer", "Bool"], mkR 46 0 46 5]
1420+ outL45 = Position 49 3 ; outSig = [ExpectHoverText ["outer", "Bool"], mkR 46 0 46 5]
1217− innL48 = Position 48 5 ; innSig = [ExpectHoverText ["inner", "Char"], mkR 49 2 49 7]
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]
1219 mkFindTests
1425 mkFindTests
1220− -- def hover look expect
1426+ -- def hover look expect
1221− [ test yes yes fffL4 fff "field in record definition"
1427+ [ test yes yes fffL4 fff "field in record definition"
1222− , test broken broken fffL8 fff "field in record construction #71"
1428+ , test broken broken fffL8 fff "field in record construction #71"
1223− , test yes yes fffL14 fff "field name used as accessor" -- 120 in Calculate.hs
1429+ , test yes yes fffL14 fff "field name used as accessor" -- 120 in Calculate.hs
1224− , test yes yes aaaL14 aaa "top-level name" -- 120
1430+ , test yes yes aaaL14 aaa "top-level name" -- 120
1225− , test yes yes dcL7 tcDC "data constructor record #247"
1431+ , test yes yes dcL7 tcDC "data constructor record #247"
1226− , test yes yes dcL12 tcDC "data constructor plain" -- 121
1432+ , test yes yes dcL12 tcDC "data constructor plain" -- 121
1227− , test yes yes tcL6 tcData "type constructor #248" -- 147
1433+ , test yes yes tcL6 tcData "type constructor #248" -- 147
1228− , test broken yes xtcL5 xtc "type constructor external #248,249"
1434+ , test broken yes xtcL5 xtc "type constructor external #248,249"
1229− , test broken yes xvL20 xvMsg "value external package #249" -- 120
1435+ , test broken yes xvL20 xvMsg "value external package #249" -- 120
1230− , test yes yes vvL16 vv "plain parameter" -- 120
1436+ , test yes yes vvL16 vv "plain parameter" -- 120
1231− , test yes yes aL18 apmp "pattern match name" -- 120
1437+ , test yes yes aL18 apmp "pattern match name" -- 120
1232− , test yes yes opL16 op "top-level operator" -- 120, 123
1438+ , test yes yes opL16 op "top-level operator" -- 120, 123
1233− , test yes yes opL18 opp "parameter operator" -- 120
1439+ , test yes yes opL18 opp "parameter operator" -- 120
1234− , test yes yes b'L19 bp "name in backticks" -- 120
1440+ , test yes yes b'L19 bp "name in backticks" -- 120
1235− , test yes yes clL23 cls "class in instance declaration #250"
1441+ , test yes yes clL23 cls "class in instance declaration #250"
1236− , test yes yes clL25 cls "class in signature #250" -- 147
1442+ , test yes yes clL25 cls "class in signature #250" -- 147
1237− , test broken yes eclL15 ecls "external class in signature #249,250"
1443+ , test broken yes eclL15 ecls "external class in signature #249,250"
1238− , test yes yes dnbL29 dnb "do-notation bind" -- 137
1444+ , test yes yes dnbL29 dnb "do-notation bind" -- 137
1239− , test yes yes dnbL30 dnb "do-notation lookup"
1445+ , test yes yes dnbL30 dnb "do-notation lookup"
1240− , test yes yes lcbL33 lcb "listcomp bind" -- 137
1446+ , test yes yes lcbL33 lcb "listcomp bind" -- 137
1241− , test yes yes lclL33 lcb "listcomp lookup"
1447+ , test yes yes lclL33 lcb "listcomp lookup"
1242− , test yes yes mclL36 mcl "top-level fn 1st clause"
1448+ , test yes yes mclL36 mcl "top-level fn 1st clause"
1243− , test yes yes mclL37 mcl "top-level fn 2nd clause #246"
1449+ , test yes yes mclL37 mcl "top-level fn 2nd clause #246"
1244− , test yes yes spaceL37 space "top-level fn on space #315"
1450+ , test yes yes spaceL37 space "top-level fn on space #315"
1245− , test no broken docL41 doc "documentation #7"
1451+ , test no broken docL41 doc "documentation #7"
1246− , test no yes eitL40 kindE "kind of Either #273"
1452+ , test no yes eitL40 kindE "kind of Either #273"
1247− , test no yes intL40 kindI "kind of Int #273"
1453+ , test no yes intL40 kindI "kind of Int #273"
1248− , test no broken tvrL40 kindV "kind of (* -> *) type variable #273"
1454+ , test no broken tvrL40 kindV "kind of (* -> *) type variable #273"
1249− , test no yes intL41 litI "literal Int in hover info #274"
1455+ , test no yes intL41 litI "literal Int in hover info #274"
1250− , test no yes chrL36 litC "literal Char in hover info #274"
1456+ , test no yes chrL36 litC "literal Char in hover info #274"
1251− , test no yes txtL8 litT "literal Text in hover info #274"
1457+ , test no yes txtL8 litT "literal Text in hover info #274"
1252− , test no yes lstL43 litL "literal List in hover info #274"
1458+ , test no yes lstL43 litL "literal List in hover info #274"
1253− , test no yes docL41 constr "type constraint in hover info #283"
1459+ , test no yes docL41 constr "type constraint in hover info #283"
1254− , test broken broken outL45 outSig "top-level signature #310"
1460+ , test broken broken outL45 outSig "top-level signature #310"
1255− , test broken broken innL48 innSig "inner 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)"
1257 where yes, broken :: (TestTree -> Maybe TestTree)
1465 where yes, broken :: (TestTree -> Maybe TestTree)
1258 yes = Just -- test should run and pass
1466 yes = Just -- test should run and pass
1259 broken = Just . (`xfail` "known broken")
1467 broken = Just . (`xfail` "known broken")
1260 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
1262pluginTests :: TestTree
1478pluginTests :: TestTree
1479+pluginTests = (`xfail8101` "known broken (#556)")
1263−pluginTests = testSessionWait "plugins" $ do
1480+ $ testSessionWait "plugins" $ do
1264 let content =
1481 let content =
1265 T.unlines
1482 T.unlines
1266 [ "{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}"
1483 [ "{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}"
@@ -1274,7 +1491,7 @@ pluginTests = testSessionWait "plugins" $ do
1274 , "foo :: Int -> Int -> Int"
1491 , "foo :: Int -> Int -> Int"
1275 , "foo a b = a + c"
1492 , "foo a b = a + c"
1277− _ <- openDoc' "Testing.hs" "haskell" content
1494+ _ <- createDoc "Testing.hs" "haskell" content
1278 expectDiagnostics
1495 expectDiagnostics
1279 [ ( "Testing.hs",
1496 [ ( "Testing.hs",
1280 [(DsError, (8, 14), "Variable not in scope: c")]
1497 [(DsError, (8, 14), "Variable not in scope: c")]
@@ -1301,7 +1518,7 @@ cppTests =
1301 run $ expectError content (2, 1)
1518 run $ expectError content (2, 1)
1303 , testSessionWait "cpp-ghcide" $ do
1520 , testSessionWait "cpp-ghcide" $ do
1304− _ <- openDoc' "A.hs" "haskell" $ T.unlines
1521+ _ <- createDoc "A.hs" "haskell" $ T.unlines
1305 ["{-# LANGUAGE CPP #-}"
1522 ["{-# LANGUAGE CPP #-}"
1306 ,"main ="
1523 ,"main ="
1307 ,"#ifdef __GHCIDE__"
1524 ,"#ifdef __GHCIDE__"
@@ -1315,7 +1532,7 @@ cppTests =
1316 expectError :: T.Text -> Cursor -> Session ()
1533 expectError :: T.Text -> Cursor -> Session ()
1317 expectError content cursor = do
1534 expectError content cursor = do
1318− _ <- openDoc' "Testing.hs" "haskell" content
1535+ _ <- createDoc "Testing.hs" "haskell" content
1319 expectDiagnostics
1536 expectDiagnostics
1320 [ ( "Testing.hs",
1537 [ ( "Testing.hs",
1321 [(DsError, cursor, "error: unterminated")]
1538 [(DsError, cursor, "error: unterminated")]
@@ -1331,13 +1548,43 @@ preprocessorTests = testSessionWait "preprocessor" $ do
1331 , "module Testing where"
1548 , "module Testing where"
1332 , "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
1334− _ <- openDoc' "Testing.hs" "haskell" content
1551+ _ <- createDoc "Testing.hs" "haskell" content
1335 expectDiagnostics
1552 expectDiagnostics
1336 [ ( "Testing.hs",
1553 [ ( "Testing.hs",
1337 [(DsError, (2, 8), "Variable not in scope: z")]
1554 [(DsError, (2, 8), "Variable not in scope: z")]
1559+safeTests :: TestTree
1563+ [ -- Test for https://github.com/digital-asset/ghcide/issues/424
1564+ testSessionWait "load" $ do
1567+ ["{-# LANGUAGE Trustworthy #-}"
1569+ ,"import System.IO.Unsafe"
1570+ ,"import System.IO"
1571+ ,"trustWorthyId :: a -> a"
1572+ ,"trustWorthyId i = unsafePerformIO $ do"
1573+ ," putStrLn \"I'm safe\""
1577+ ["{-# LANGUAGE Safe #-}"
1580+ ,"safeId :: a -> a"
1581+ ,"safeId = trustWorthyId"
1584+ _ <- createDoc "A.hs" "haskell" sourceA
1585+ _ <- createDoc "B.hs" "haskell" sourceB
1586+ expectNoMoreDiagnostics 1 ]
1341thTests :: TestTree
1588thTests :: TestTree
1342thTests =
1589thTests =
1343 testGroup
1590 testGroup
@@ -1363,9 +1610,30 @@ thTests =
1363 "b :: Integer",
1610 "b :: Integer",
1364 "b = $(litE $ IntegerL $ a) + n"
1611 "b = $(litE $ IntegerL $ a) + n"
1366− _ <- openDoc' "A.hs" "haskell" sourceA
1613+ _ <- createDoc "A.hs" "haskell" sourceA
1367− _ <- openDoc' "B.hs" "haskell" sourceB
1614+ _ <- createDoc "B.hs" "haskell" sourceB
1368 expectDiagnostics [ ( "B.hs", [(DsError, (6, 29), "Variable not in scope: n")] ) ]
1615 expectDiagnostics [ ( "B.hs", [(DsError, (6, 29), "Variable not in scope: n")] ) ]
1616+ , testSessionWait "newtype-closure" $ do
1619+ [ "{-# LANGUAGE DeriveDataTypeable #-}"
1620+ ,"{-# LANGUAGE TemplateHaskell #-}"
1621+ ,"module A (a) where"
1622+ ,"import Data.Data"
1623+ ,"import Language.Haskell.TH"
1624+ ,"newtype A = A () deriving (Data)"
1629+ [ "{-# LANGUAGE TemplateHaskell #-}"
1634+ _ <- createDoc "A.hs" "haskell" sourceA
1635+ _ <- createDoc "B.hs" "haskell" sourceB
1371completionTests :: TestTree
1639completionTests :: TestTree
@@ -1373,19 +1641,19 @@ completionTests
1373 = testGroup "completion"
1641 = testGroup "completion"
1374 [ testSessionWait "variable" $ do
1642 [ testSessionWait "variable" $ do
1375 let source = T.unlines ["module A where", "f = hea"]
1643 let source = T.unlines ["module A where", "f = hea"]
1376− docId <- openDoc' "A.hs" "haskell" source
1644+ docId <- createDoc "A.hs" "haskell" source
1377 compls <- getCompletions docId (Position 1 7)
1645 compls <- getCompletions docId (Position 1 7)
1378 liftIO $ map dropDocs compls @?=
1646 liftIO $ map dropDocs compls @?=
1379 [complItem "head" (Just CiFunction) (Just "[a] -> a")]
1647 [complItem "head" (Just CiFunction) (Just "[a] -> a")]
1380 let [CompletionItem { _documentation = headDocs}] = compls
1648 let [CompletionItem { _documentation = headDocs}] = compls
1381 checkDocText "head" headDocs [ "Defined in 'Prelude'"
1649 checkDocText "head" headDocs [ "Defined in 'Prelude'"
1382−#if MIN_GHC_API_VERSION(8,6,0)
1650+#if MIN_GHC_API_VERSION(8,6,5)
1383 , "Extract the first element of a list"
1651 , "Extract the first element of a list"
1386 , testSessionWait "constructor" $ do
1654 , testSessionWait "constructor" $ do
1387 let source = T.unlines ["module A where", "f = Tru"]
1655 let source = T.unlines ["module A where", "f = Tru"]
1388− docId <- openDoc' "A.hs" "haskell" source
1656+ docId <- createDoc "A.hs" "haskell" source
1389 compls <- getCompletions docId (Position 1 7)
1657 compls <- getCompletions docId (Position 1 7)
1390 liftIO $ map dropDocs compls @?=
1658 liftIO $ map dropDocs compls @?=
1391 [ complItem "True" (Just CiConstructor) (Just "Bool")
1659 [ complItem "True" (Just CiConstructor) (Just "Bool")
@@ -1397,7 +1665,7 @@ completionTests
1398 , testSessionWait "type" $ do
1666 , testSessionWait "type" $ do
1399 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 = ()"]
1400− docId <- openDoc' "A.hs" "haskell" source
1668+ docId <- createDoc "A.hs" "haskell" source
1401 expectDiagnostics [ ("A.hs", [(DsWarning, (3,0), "not used")]) ]
1669 expectDiagnostics [ ("A.hs", [(DsWarning, (3,0), "not used")]) ]
1402 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"]]
1403 compls <- getCompletions docId (Position 2 7)
1671 compls <- getCompletions docId (Position 2 7)
@@ -1407,14 +1675,14 @@ completionTests
1407 let [ CompletionItem { _documentation = boundedDocs},
1675 let [ CompletionItem { _documentation = boundedDocs},
1408 CompletionItem { _documentation = boolDocs } ] = compls
1676 CompletionItem { _documentation = boolDocs } ] = compls
1409 checkDocText "Bounded" boundedDocs [ "Defined in 'Prelude'"
1677 checkDocText "Bounded" boundedDocs [ "Defined in 'Prelude'"
1410−#if MIN_GHC_API_VERSION(8,6,0)
1678+#if MIN_GHC_API_VERSION(8,6,5)
1411 , "name the upper and lower limits"
1679 , "name the upper and lower limits"
1414 checkDocText "Bool" boolDocs [ "Defined in 'Prelude'" ]
1682 checkDocText "Bool" boolDocs [ "Defined in 'Prelude'" ]
1415 , testSessionWait "qualified" $ do
1683 , testSessionWait "qualified" $ do
1416 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 = ()"]
1417− docId <- openDoc' "A.hs" "haskell" source
1685+ docId <- createDoc "A.hs" "haskell" source
1418 expectDiagnostics [ ("A.hs", [(DsWarning, (2, 0), "not used")]) ]
1686 expectDiagnostics [ ("A.hs", [(DsWarning, (2, 0), "not used")]) ]
1419 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"]]
1420 compls <- getCompletions docId (Position 2 15)
1688 compls <- getCompletions docId (Position 2 15)
@@ -1422,15 +1690,36 @@ completionTests
1422 [complItem "head" (Just CiFunction) (Just "[a] -> a")]
1690 [complItem "head" (Just CiFunction) (Just "[a] -> a")]
1423 let [CompletionItem { _documentation = headDocs}] = compls
1691 let [CompletionItem { _documentation = headDocs}] = compls
1424 checkDocText "head" headDocs [ "Defined in 'Prelude'"
1692 checkDocText "head" headDocs [ "Defined in 'Prelude'"
1425−#if MIN_GHC_API_VERSION(8,6,0)
1693+#if MIN_GHC_API_VERSION(8,6,5)
1426 , "Extract the first element of a list"
1694 , "Extract the first element of a list"
1429 , testSessionWait "keyword" $ do
1697 , testSessionWait "keyword" $ do
1430 let source = T.unlines ["module A where", "f = newty"]
1698 let source = T.unlines ["module A where", "f = newty"]
1431− docId <- openDoc' "A.hs" "haskell" source
1699+ docId <- createDoc "A.hs" "haskell" source
1432 compls <- getCompletions docId (Position 1 9)
1700 compls <- getCompletions docId (Position 1 9)
1433 liftIO $ compls @?= [keywordItem "newtype"]
1701 liftIO $ compls @?= [keywordItem "newtype"]
1702+ , testSessionWait "type context" $ do
1703+ let source = T.unlines
1704+ [ "{-# OPTIONS_GHC -Wunused-binds #-}"
1705+ , "module A () where"
1708+ docId <- createDoc "A.hs" "haskell" source
1709+ expectDiagnostics [("A.hs", [(DsWarning, (2, 0), "not used")])]
1711+ [ TextDocumentContentChangeEvent Nothing Nothing $ T.unlines
1712+ [ "{-# OPTIONS_GHC -Wunused-binds #-}"
1713+ , "module A () where"
1718+ -- At this point the module parses but does not typecheck.
1719+ -- This should be sufficient to detect that we are in a
1720+ -- type context and only show the completion to the type.
1721+ compls <- getCompletions docId (Position 3 11)
1722+ liftIO $ map dropDocs compls @?= [complItem "Integer"(Just CiStruct) (Just "*")]
1436 dropDocs :: CompletionItem -> CompletionItem
1725 dropDocs :: CompletionItem -> CompletionItem
@@ -1438,6 +1727,7 @@ completionTests
1438 complItem label kind ty = CompletionItem
1727 complItem label kind ty = CompletionItem
1439 { _label = label
1728 { _label = label
1440 , _kind = kind
1729 , _kind = kind
1441 , _detail = (":: " <>) <$> ty
1731 , _detail = (":: " <>) <$> ty
1442 , _documentation = Nothing
1732 , _documentation = Nothing
1443 , _deprecated = Nothing
1733 , _deprecated = Nothing
@@ -1455,6 +1745,7 @@ completionTests
1455 keywordItem label = CompletionItem
1745 keywordItem label = CompletionItem
1456 { _label = label
1746 { _label = label
1457 , _kind = Just CiKeyword
1747 , _kind = Just CiKeyword
1458 , _detail = Nothing
1749 , _detail = Nothing
1459 , _documentation = Nothing
1750 , _documentation = Nothing
1460 , _deprecated = Nothing
1751 , _deprecated = Nothing
@@ -1482,7 +1773,7 @@ outlineTests = testGroup
1482 "outline"
1773 "outline"
1483 [ testSessionWait "type class" $ do
1774 [ testSessionWait "type class" $ do
1484 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"]
1485− docId <- openDoc' "A.hs" "haskell" source
1776+ docId <- createDoc "A.hs" "haskell" source
1486 symbols <- getDocumentSymbols docId
1777 symbols <- getDocumentSymbols docId
1487 liftIO $ symbols @?= Left
1778 liftIO $ symbols @?= Left
1488 [ moduleSymbol
1779 [ moduleSymbol
@@ -1495,7 +1786,7 @@ outlineTests = testGroup
1496 , testSessionWait "type class instance " $ do
1787 , testSessionWait "type class instance " $ do
1497 let source = T.unlines ["class A a where", "instance A () where"]
1788 let source = T.unlines ["class A a where", "instance A () where"]
1498− docId <- openDoc' "A.hs" "haskell" source
1789+ docId <- createDoc "A.hs" "haskell" source
1499 symbols <- getDocumentSymbols docId
1790 symbols <- getDocumentSymbols docId
1500 liftIO $ symbols @?= Left
1791 liftIO $ symbols @?= Left
1501 [ classSymbol "A a" (R 0 0 0 15) []
1792 [ classSymbol "A a" (R 0 0 0 15) []
@@ -1503,7 +1794,7 @@ outlineTests = testGroup
1504 , testSessionWait "type family" $ do
1795 , testSessionWait "type family" $ do
1505 let source = T.unlines ["{-# language TypeFamilies #-}", "type family A"]
1796 let source = T.unlines ["{-# language TypeFamilies #-}", "type family A"]
1506− docId <- openDoc' "A.hs" "haskell" source
1797+ docId <- createDoc "A.hs" "haskell" source
1507 symbols <- getDocumentSymbols docId
1798 symbols <- getDocumentSymbols docId
1508 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)]
1509 , testSessionWait "type family instance " $ do
1800 , testSessionWait "type family instance " $ do
@@ -1512,7 +1803,7 @@ outlineTests = testGroup
1512 , "type family A a"
1803 , "type family A a"
1513 , "type instance A () = ()"
1804 , "type instance A () = ()"
1515− docId <- openDoc' "A.hs" "haskell" source
1806+ docId <- createDoc "A.hs" "haskell" source
1516 symbols <- getDocumentSymbols docId
1807 symbols <- getDocumentSymbols docId
1517 liftIO $ symbols @?= Left
1808 liftIO $ symbols @?= Left
1518 [ docSymbolD "A a" "type family" SkClass (R 1 0 1 15)
1809 [ docSymbolD "A a" "type family" SkClass (R 1 0 1 15)
@@ -1520,7 +1811,7 @@ outlineTests = testGroup
1521 , testSessionWait "data family" $ do
1812 , testSessionWait "data family" $ do
1522 let source = T.unlines ["{-# language TypeFamilies #-}", "data family A"]
1813 let source = T.unlines ["{-# language TypeFamilies #-}", "data family A"]
1523− docId <- openDoc' "A.hs" "haskell" source
1814+ docId <- createDoc "A.hs" "haskell" source
1524 symbols <- getDocumentSymbols docId
1815 symbols <- getDocumentSymbols docId
1525 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)]
1526 , testSessionWait "data family instance " $ do
1817 , testSessionWait "data family instance " $ do
@@ -1529,7 +1820,7 @@ outlineTests = testGroup
1529 , "data family A a"
1820 , "data family A a"
1530 , "data instance A () = A ()"
1821 , "data instance A () = A ()"
1532− docId <- openDoc' "A.hs" "haskell" source
1823+ docId <- createDoc "A.hs" "haskell" source
1533 symbols <- getDocumentSymbols docId
1824 symbols <- getDocumentSymbols docId
1534 liftIO $ symbols @?= Left
1825 liftIO $ symbols @?= Left
1535 [ docSymbolD "A a" "data family" SkClass (R 1 0 1 11)
1826 [ docSymbolD "A a" "data family" SkClass (R 1 0 1 11)
@@ -1537,36 +1828,36 @@ outlineTests = testGroup
1538 , testSessionWait "constant" $ do
1829 , testSessionWait "constant" $ do
1539 let source = T.unlines ["a = ()"]
1830 let source = T.unlines ["a = ()"]
1540− docId <- openDoc' "A.hs" "haskell" source
1831+ docId <- createDoc "A.hs" "haskell" source
1541 symbols <- getDocumentSymbols docId
1832 symbols <- getDocumentSymbols docId
1542 liftIO $ symbols @?= Left
1833 liftIO $ symbols @?= Left
1543 [docSymbol "a" SkFunction (R 0 0 0 6)]
1834 [docSymbol "a" SkFunction (R 0 0 0 6)]
1544 , testSessionWait "pattern" $ do
1835 , testSessionWait "pattern" $ do
1545 let source = T.unlines ["Just foo = Just 21"]
1836 let source = T.unlines ["Just foo = Just 21"]
1546− docId <- openDoc' "A.hs" "haskell" source
1837+ docId <- createDoc "A.hs" "haskell" source
1547 symbols <- getDocumentSymbols docId
1838 symbols <- getDocumentSymbols docId
1548 liftIO $ symbols @?= Left
1839 liftIO $ symbols @?= Left
1549 [docSymbol "Just foo" SkFunction (R 0 0 0 18)]
1840 [docSymbol "Just foo" SkFunction (R 0 0 0 18)]
1550 , testSessionWait "pattern with type signature" $ do
1841 , testSessionWait "pattern with type signature" $ do
1551 let source = T.unlines ["{-# language ScopedTypeVariables #-}", "a :: () = ()"]
1842 let source = T.unlines ["{-# language ScopedTypeVariables #-}", "a :: () = ()"]
1552− docId <- openDoc' "A.hs" "haskell" source
1843+ docId <- createDoc "A.hs" "haskell" source
1553 symbols <- getDocumentSymbols docId
1844 symbols <- getDocumentSymbols docId
1554 liftIO $ symbols @?= Left
1845 liftIO $ symbols @?= Left
1555 [docSymbol "a :: ()" SkFunction (R 1 0 1 12)]
1846 [docSymbol "a :: ()" SkFunction (R 1 0 1 12)]
1556 , testSessionWait "function" $ do
1847 , testSessionWait "function" $ do
1557 let source = T.unlines ["a x = ()"]
1848 let source = T.unlines ["a x = ()"]
1558− docId <- openDoc' "A.hs" "haskell" source
1849+ docId <- createDoc "A.hs" "haskell" source
1559 symbols <- getDocumentSymbols docId
1850 symbols <- getDocumentSymbols docId
1560 liftIO $ symbols @?= Left [docSymbol "a" SkFunction (R 0 0 0 8)]
1851 liftIO $ symbols @?= Left [docSymbol "a" SkFunction (R 0 0 0 8)]
1561 , testSessionWait "type synonym" $ do
1852 , testSessionWait "type synonym" $ do
1562 let source = T.unlines ["type A = Bool"]
1853 let source = T.unlines ["type A = Bool"]
1563− docId <- openDoc' "A.hs" "haskell" source
1854+ docId <- createDoc "A.hs" "haskell" source
1564 symbols <- getDocumentSymbols docId
1855 symbols <- getDocumentSymbols docId
1565 liftIO $ symbols @?= Left
1856 liftIO $ symbols @?= Left
1566 [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)]
1567 , testSessionWait "datatype" $ do
1858 , testSessionWait "datatype" $ do
1568 let source = T.unlines ["data A = C"]
1859 let source = T.unlines ["data A = C"]
1569− docId <- openDoc' "A.hs" "haskell" source
1860+ docId <- createDoc "A.hs" "haskell" source
1570 symbols <- getDocumentSymbols docId
1861 symbols <- getDocumentSymbols docId
1571 liftIO $ symbols @?= Left
1862 liftIO $ symbols @?= Left
1572 [ docSymbolWithChildren "A"
1863 [ docSymbolWithChildren "A"
@@ -1574,18 +1865,47 @@ outlineTests = testGroup
1574 (R 0 0 0 10)
1865 (R 0 0 0 10)
1575 [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)
1577 , testSessionWait "import" $ do
1880 , testSessionWait "import" $ do
1578 let source = T.unlines ["import Data.Maybe"]
1881 let source = T.unlines ["import Data.Maybe"]
1579− docId <- openDoc' "A.hs" "haskell" source
1882+ docId <- createDoc "A.hs" "haskell" source
1580 symbols <- getDocumentSymbols docId
1883 symbols <- getDocumentSymbols docId
1581 liftIO $ symbols @?= Left
1884 liftIO $ symbols @?= Left
1885+ [docSymbolWithChildren "imports"
1582− [docSymbol "import Data.Maybe" SkModule (R 0 0 0 17)]
1888+ [ docSymbol "import Data.Maybe" SkModule (R 0 0 0 17)
1891+ , testSessionWait "multiple import" $ do
1892+ let source = T.unlines ["", "import Data.Maybe", "", "import Control.Exception", ""]
1893+ docId <- createDoc "A.hs" "haskell" source
1894+ symbols <- getDocumentSymbols docId
1895+ liftIO $ symbols @?= Left
1896+ [docSymbolWithChildren "imports"
1899+ [ docSymbol "import Data.Maybe" SkModule (R 1 0 1 17)
1900+ , docSymbol "import Control.Exception" SkModule (R 3 0 3 24)
1583 , testSessionWait "foreign import" $ do
1903 , testSessionWait "foreign import" $ do
1584 let source = T.unlines
1904 let source = T.unlines
1585 [ "{-# language ForeignFunctionInterface #-}"
1905 [ "{-# language ForeignFunctionInterface #-}"
1586 , "foreign import ccall \"a\" a :: Int"
1906 , "foreign import ccall \"a\" a :: Int"
1588− docId <- openDoc' "A.hs" "haskell" source
1908+ docId <- createDoc "A.hs" "haskell" source
1589 symbols <- getDocumentSymbols docId
1909 symbols <- getDocumentSymbols docId
1590 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)]
1591 , testSessionWait "foreign export" $ do
1911 , testSessionWait "foreign export" $ do
@@ -1593,7 +1913,7 @@ outlineTests = testGroup
1593 [ "{-# language ForeignFunctionInterface #-}"
1913 [ "{-# language ForeignFunctionInterface #-}"
1594 , "foreign export ccall odd :: Int -> Bool"
1914 , "foreign export ccall odd :: Int -> Bool"
1596− docId <- openDoc' "A.hs" "haskell" source
1916+ docId <- createDoc "A.hs" "haskell" source
1597 symbols <- getDocumentSymbols docId
1917 symbols <- getDocumentSymbols docId
1598 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)]
@@ -1606,6 +1926,8 @@ outlineTests = testGroup
1606 DocumentSymbol name (Just detail) kind Nothing loc loc Nothing
1926 DocumentSymbol name (Just detail) kind Nothing loc loc Nothing
1607 docSymbolWithChildren name kind loc cc =
1927 docSymbolWithChildren name kind loc cc =
1608 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)
1609 moduleSymbol name loc cc = DocumentSymbol name
1931 moduleSymbol name loc cc = DocumentSymbol name
@@ -1627,8 +1949,16 @@ pattern R x y x' y' = Range (Position x y) (Position x' y')
1627xfail :: TestTree -> String -> TestTree
1949xfail :: TestTree -> String -> TestTree
1628xfail = flip expectFailBecause
1950xfail = flip expectFailBecause
1952+xfail8101 :: TestTree -> String -> TestTree
1953+#if MIN_GHC_API_VERSION(8,10,0)
1954+xfail8101 = flip expectFailBecause
1630data Expect
1959data Expect
1631 = ExpectRange Range -- Both gotoDef and hover should report this range
1960 = ExpectRange Range -- Both gotoDef and hover should report this range
1961+ | ExpectLocation Location
1632-- | ExpectDefRange Range -- Only gotoDef should report this range
1962-- | ExpectDefRange Range -- Only gotoDef should report this range
1633 | ExpectHoverRange Range -- Only hover should report this range
1963 | ExpectHoverRange Range -- Only hover should report this range
1634 | ExpectHoverText [T.Text] -- the hover message must contain these snippets
1964 | ExpectHoverText [T.Text] -- the hover message must contain these snippets
@@ -1641,6 +1971,9 @@ data Expect
1641mkR :: Int -> Int -> Int -> Int -> Expect
1971mkR :: Int -> Int -> Int -> Int -> Expect
1642mkR startLine startColumn endLine endColumn = ExpectRange $ mkRange startLine startColumn endLine endColumn
1972mkR startLine startColumn endLine endColumn = ExpectRange $ mkRange startLine startColumn endLine endColumn
1974+mkL :: Uri -> Int -> Int -> Int -> Int -> Expect
1975+mkL uri startLine startColumn endLine endColumn = ExpectLocation $ Location uri $ mkRange startLine startColumn endLine endColumn
1644haddockTests :: TestTree
1977haddockTests :: TestTree
1645haddockTests
1978haddockTests
1646 = testGroup "haddock"
1979 = testGroup "haddock"
@@ -1657,11 +1990,7 @@ haddockTests
1657 (unlines
1990 (unlines
1660−#if MIN_VERSION_haddock_library(1,8,0)
1661 , "However, `(+)` and `(*)` are"
1993 , "However, `(+)` and `(*)` are"
1663− , "However, '(+)' and '(*)' are"
1665 , "customarily expected to define a ring and have the following properties: "
1994 , "customarily expected to define a ring and have the following properties: "
1666 , "+ ****Associativity of (+)****: `(x + y) + z` = `x + (y + z)`"
1995 , "+ ****Associativity of (+)****: `(x + y) + z` = `x + (y + z)`"
1667 , "+ ****Commutativity of (+)****: `x + y` = `y + x`"
1996 , "+ ****Commutativity of (+)****: `x + y` = `y + x`"
@@ -1700,6 +2029,167 @@ haddockTests
1701 checkHaddock s txt = spanDocToMarkdownForTest s @?= txt
2030 checkHaddock s txt = spanDocToMarkdownForTest s @?= txt
2032+cradleTests :: TestTree
2033+cradleTests = testGroup "cradle"
2034+ [testGroup "dependencies" [sessionDepsArePickedUp]
2035+ ,testGroup "loading" [loadCradleOnlyonce]
2036+ ,testGroup "multi" [simpleMultiTest, simpleMultiTest2]
2039+loadCradleOnlyonce :: TestTree
2040+loadCradleOnlyonce = testGroup "load cradle only once"
2041+ [ testSession' "implicit" implicit
2042+ , testSession' "direct" direct
2046+ liftIO $ writeFileUTF8 (dir </> "hie.yaml")
2047+ "cradle: {direct: {arguments: []}}"
2049+ implicit dir = test dir
2051+ doc <- createDoc "B.hs" "haskell" "module B where\nimport Data.Foo"
2052+ msgs <- someTill (skipManyTill anyMessage cradleLoadedMessage) (skipManyTill anyMessage (message @PublishDiagnosticsNotification))
2053+ liftIO $ length msgs @?= 1
2054+ changeDoc doc [TextDocumentContentChangeEvent Nothing Nothing "module B where\nimport Data.Maybe"]
2055+ msgs <- manyTill (skipManyTill anyMessage cradleLoadedMessage) (skipManyTill anyMessage (message @PublishDiagnosticsNotification))
2056+ liftIO $ length msgs @?= 0
2057+ _ <- createDoc "A.hs" "haskell" "module A where\nimport LoadCradleBar"
2058+ msgs <- manyTill (skipManyTill anyMessage cradleLoadedMessage) (skipManyTill anyMessage (message @PublishDiagnosticsNotification))
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", [])]
2098+cradleLoadedMessage :: Session FromServerMessage
2099+cradleLoadedMessage = satisfy $ \case
2100+ NotCustomServer (NotificationMessage _ (CustomServerMethod m) _) -> m == cradleLoadedMethod
2103+cradleLoadedMethod :: T.Text
2104+cradleLoadedMethod = "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
2156+sessionDepsArePickedUp :: TestTree
2157+sessionDepsArePickedUp = testSession'
2158+ "session-deps-are-picked-up"
2162+ (dir </> "hie.yaml")
2163+ "cradle: {direct: {arguments: []}}"
2164+ -- Open without OverloadedStrings and expect an error.
2165+ doc <- createDoc "Foo.hs" "haskell" fooContent
2167+ [("Foo.hs", [(DsError, (3, 6), "Couldn't match expected type")])]
2168+ -- Update hie.yaml to enable OverloadedStrings.
2171+ (dir </> "hie.yaml")
2172+ "cradle: {direct: {arguments: [-XOverloadedStrings]}}"
2173+ -- Send change event.
2175+ TextDocumentContentChangeEvent
2176+ { _range = Just (Range (Position 4 0) (Position 4 0)),
2177+ _rangeLength = Nothing,
2180+ changeDoc doc [change]
2182+ expectDiagnostics [("Foo.hs", [])]
2186+ [ "module Foo where",
2187+ "import Data.Text",
1703----------------------------------------------------------------------
2193----------------------------------------------------------------------
@@ -1707,6 +2197,12 @@ haddockTests
1707testSession :: String -> Session () -> TestTree
2197testSession :: String -> Session () -> TestTree
1708testSession name = testCase name . run
2198testSession name = testCase name . run
2200+testSessionWithExtraFiles :: FilePath -> String -> (FilePath -> Session ()) -> TestTree
2201+testSessionWithExtraFiles prefix name = testCase name . runWithExtraFiles prefix
2203+testSession' :: String -> (FilePath -> Session ()) -> TestTree
2204+testSession' name = testCase name . run'
1710testSessionWait :: String -> Session () -> TestTree
2206testSessionWait :: String -> Session () -> TestTree
1711testSessionWait name = testSession name .
2207testSessionWait name = testSession name .
1712 -- Check that any diagnostics produced were already consumed by the test case.
2208 -- Check that any diagnostics produced were already consumed by the test case.
@@ -1717,24 +2213,53 @@ testSessionWait name = testSession name .
1717 -- Experimentally, 0.5s seems to be long enough to wait for any final diagnostics to appear.
2213 -- Experimentally, 0.5s seems to be long enough to wait for any final diagnostics to appear.
1718 ( >> expectNoMoreDiagnostics 0.5)
2214 ( >> expectNoMoreDiagnostics 0.5)
1720−pickActionWithTitle :: T.Text -> [CAResult] -> CodeAction
2216+pickActionWithTitle :: T.Text -> [CAResult] -> IO CodeAction
1721−pickActionWithTitle title actions = head
2217+pickActionWithTitle title actions = do
2218+ assertBool ("Found no matching actions: " <> show titles) (not $ null matches)
2219+ return $ head matches
2223+ | CACodeAction CodeAction { _title = actionTitle } <- actions
1722− [ action
2226+ [ action
1723− | CACodeAction action@CodeAction{ _title = actionTitle } <- actions
2227+ | CACodeAction action@CodeAction { _title = actionTitle } <- actions
1724− , title == actionTitle ]
2228+ , title == actionTitle
1726mkRange :: Int -> Int -> Int -> Int -> Range
2231mkRange :: Int -> Int -> Int -> Int -> Range
1727mkRange a b c d = Range (Position a b) (Position c d)
2232mkRange a b c d = Range (Position a b) (Position c d)
1729run :: Session a -> IO a
2234run :: Session a -> IO a
1730−run s = withTempDir $ \dir -> do
2235+run 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)
2250+run' :: (FilePath -> Session a) -> IO a
2251+run' s = withTempDir $ \dir -> runInDir dir (s dir)
2253+runInDir :: FilePath -> Session a -> IO a
1731 ghcideExe <- locateGhcideExecutable
2255 ghcideExe <- locateGhcideExecutable
1733 -- Temporarily hack around https://github.com/mpickering/hie-bios/pull/56
2257 -- Temporarily hack around https://github.com/mpickering/hie-bios/pull/56
1734 -- 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
1735 createDirectoryIfMissing True $ dir ++ "/Data"
2259 createDirectoryIfMissing True $ dir ++ "/Data"
1737− let cmd = unwords [ghcideExe, "--lsp", "--cwd", dir]
2262+ let cmd = unwords [ghcideExe, "--lsp", "--test", "--cwd", dir]
1738 -- HIE calls getXgdDirectory which assumes that HOME is set.
2263 -- HIE calls getXgdDirectory which assumes that HOME is set.
1739 -- Only sets HOME if it wasn't already set.
2264 -- Only sets HOME if it wasn't already set.
1740 setEnv "HOME" "/homeless-shelter" False
2265 setEnv "HOME" "/homeless-shelter" False
@@ -1751,7 +2276,7 @@ run s = withTempDir $ \dir -> do
1751openTestDataDoc :: FilePath -> Session TextDocumentIdentifier
2276openTestDataDoc :: FilePath -> Session TextDocumentIdentifier
1752openTestDataDoc path = do
2277openTestDataDoc path = do
1753 source <- liftIO $ readFileUtf8 $ "test/data" </> path
2278 source <- liftIO $ readFileUtf8 $ "test/data" </> path
1754− openDoc' path "haskell" source
2279+ createDoc path "haskell" source
1756findCodeActions :: TextDocumentIdentifier -> Range -> [T.Text] -> Session [CodeAction]
2281findCodeActions :: TextDocumentIdentifier -> Range -> [T.Text] -> Session [CodeAction]
1757findCodeActions doc range expectedTitles = do
2282findCodeActions doc range expectedTitles = do
@@ -1766,7 +2291,7 @@ findCodeActions doc range expectedTitles = do
1766 [ actionTitle
2291 [ actionTitle
1767 | CACodeAction CodeAction { _title = actionTitle } <- actions
2292 | CACodeAction CodeAction { _title = actionTitle } <- actions
1769− ++ "is not a superset of "
2294+ ++ " is not a superset of "
1770 ++ show expectedTitles
2295 ++ show expectedTitles
1771 liftIO $ case matches of
2296 liftIO $ case matches of
1772 Nothing -> assertFailure msg
2297 Nothing -> assertFailure msg
@@ -1779,13 +2304,193 @@ findCodeAction doc range t = head <$> findCodeActions doc range [t]
1779unitTests :: TestTree
2304unitTests :: TestTree
1780unitTests = do
2305unitTests = do
1781 testGroup "Unit"
2306 testGroup "Unit"
1782− [ testCase "empty file path" $
2307+ [ testCase "empty file path does NOT work with the empty String literal" $
1783− uriToFilePath' (fromNormalizedUri $ filePathToUri' "") @?= Just ""
2308+ uriToFilePath' (fromNormalizedUri $ filePathToUri' "") @?= Just "."
2309+ , testCase "empty file path works using toNormalizedFilePath'" $
2310+ uriToFilePath' (fromNormalizedUri $ filePathToUri' (toNormalizedFilePath' "")) @?= Just ""
2311+ , testCase "empty path URI" $ do
2312+ Just URI{..} <- pure $ parseURI (T.unpack $ getUri $ fromNormalizedUri emptyPathUri)
2313+ uriScheme @?= "file:"
2315+ , testCase "from empty path URI" $ do
2316+ let uri = Uri "file://"
2317+ uriToFilePath' uri @?= Just ""
1786−-- | Wrapper around 'LSPTest.openDoc'' that sends file creation events
2320+positionMappingTests :: TestTree
1787−openDoc' :: FilePath -> String -> T.Text -> Session TextDocumentIdentifier
2321+positionMappingTests =
1788−openDoc' fp name contents = do
2322+ testGroup "position mapping"
1789− res@(TextDocumentIdentifier uri) <- LSPTest.openDoc' fp name contents
2323+ [ testGroup "toCurrent"
1790− sendNotification WorkspaceDidChangeWatchedFiles (DidChangeWatchedFilesParams $ List [FileEvent uri FcCreated])
2324+ [ testCase "before" $
2326+ (Range (Position 0 1) (Position 0 3))
2328+ (Position 0 0) @?= Just (Position 0 0)
2329+ , testCase "after, same line, same length" $
2331+ (Range (Position 0 1) (Position 0 3))
2333+ (Position 0 3) @?= Just (Position 0 3)
2334+ , testCase "after, same line, increased length" $
2336+ (Range (Position 0 1) (Position 0 3))
2338+ (Position 0 3) @?= Just (Position 0 4)
2339+ , testCase "after, same line, decreased length" $
2341+ (Range (Position 0 1) (Position 0 3))
2343+ (Position 0 3) @?= Just (Position 0 2)
2344+ , testCase "after, next line, no newline" $
2346+ (Range (Position 0 1) (Position 0 3))
2348+ (Position 1 3) @?= Just (Position 1 3)
2349+ , testCase "after, next line, newline" $
2351+ (Range (Position 0 1) (Position 0 3))
2353+ (Position 1 0) @?= Just (Position 2 0)
2354+ , testCase "after, same line, newline" $
2356+ (Range (Position 0 1) (Position 0 3))
2358+ (Position 0 4) @?= Just (Position 1 2)
2359+ , testCase "after, same line, newline + newline at end" $
2361+ (Range (Position 0 1) (Position 0 3))
2363+ (Position 0 4) @?= Just (Position 2 1)
2364+ , testCase "after, same line, newline + newline at end" $
2366+ (Range (Position 0 1) (Position 0 1))
2368+ (Position 0 1) @?= Just (Position 0 4)
2370+ , testGroup "fromCurrent"
2371+ [ testCase "before" $
2373+ (Range (Position 0 1) (Position 0 3))
2375+ (Position 0 0) @?= Just (Position 0 0)
2376+ , testCase "after, same line, same length" $
2378+ (Range (Position 0 1) (Position 0 3))
2380+ (Position 0 3) @?= Just (Position 0 3)
2381+ , testCase "after, same line, increased length" $
2383+ (Range (Position 0 1) (Position 0 3))
2385+ (Position 0 4) @?= Just (Position 0 3)
2386+ , testCase "after, same line, decreased length" $
2388+ (Range (Position 0 1) (Position 0 3))
2390+ (Position 0 2) @?= Just (Position 0 3)
2391+ , testCase "after, next line, no newline" $
2393+ (Range (Position 0 1) (Position 0 3))
2395+ (Position 1 3) @?= Just (Position 1 3)
2396+ , testCase "after, next line, newline" $
2398+ (Range (Position 0 1) (Position 0 3))
2400+ (Position 2 0) @?= Just (Position 1 0)
2401+ , testCase "after, same line, newline" $
2403+ (Range (Position 0 1) (Position 0 3))
2405+ (Position 1 2) @?= Just (Position 0 4)
2406+ , testCase "after, same line, newline + newline at end" $
2408+ (Range (Position 0 1) (Position 0 3))
2410+ (Position 2 1) @?= Just (Position 0 4)
2411+ , testCase "after, same line, newline + newline at end" $
2413+ (Range (Position 0 1) (Position 0 1))
2415+ (Position 0 4) @?= Just (Position 0 1)
2417+ , adjustOption (\(QuickCheckTests i) -> QuickCheckTests (max 1000 i)) $ testGroup "properties"
2418+ [ testProperty "fromCurrent r t <=< toCurrent r t" $ do
2419+ -- Note that it is important to use suchThatMap on all values at once
2420+ -- instead of only using it on the position. Otherwise you can get
2421+ -- into situations where there is no position that can be mapped back
2422+ -- for the edit which will result in QuickCheck looping forever.
2425+ range <- genRange rope
2426+ PrintableText replacement <- arbitrary
2427+ oldPos <- genPosition rope
2428+ pure (range, replacement, oldPos)
2431+ (\(range, replacement, oldPos) -> (range, replacement, oldPos,) <$> toCurrent range replacement oldPos)) $
2432+ \(range, replacement, oldPos, newPos) ->
2433+ fromCurrent range replacement newPos === Just oldPos
2434+ , testProperty "toCurrent r t <=< fromCurrent r t" $ do
2437+ range <- genRange rope
2438+ PrintableText replacement <- arbitrary
2439+ let newRope = applyChange rope (TextDocumentContentChangeEvent (Just range) Nothing replacement)
2440+ newPos <- genPosition newRope
2441+ pure (range, replacement, newPos)
2444+ (\(range, replacement, newPos) -> (range, replacement, newPos,) <$> fromCurrent range replacement newPos)) $
2445+ \(range, replacement, newPos, oldPos) ->
2446+ toCurrent range replacement oldPos === Just newPos
2450+newtype PrintableText = PrintableText { getPrintableText :: T.Text }
2453+instance Arbitrary PrintableText where
2454+ arbitrary = PrintableText . T.pack . getPrintableString <$> arbitrary
2458+genRope = Rope.fromText . getPrintableText <$> arbitrary
2460+genPosition :: Rope -> Gen Position
2462+ row <- choose (0, max 0 $ rows - 1)
2463+ let columns = Rope.columns (nthLine row r)
2464+ column <- choose (0, max 0 $ columns - 1)
2465+ pure $ Position row column
2466+ where rows = Rope.rows r
2468+genRange :: Rope -> Gen Range
2470+ startPos@(Position startLine startColumn) <- genPosition r
2471+ let maxLineDiff = max 0 $ rows - 1 - startLine
2472+ endLine <- choose (startLine, startLine + maxLineDiff)
2473+ let columns = Rope.columns (nthLine endLine r)
2475+ if startLine == endLine
2476+ then choose (startColumn, columns)
2477+ else choose (0, max 0 $ columns - 1)
2478+ pure $ Range startPos (Position endLine endColumn)
2479+ where rows = Rope.rows r
2481+-- | Get the ith line of a rope, starting from 0. Trailing newline not included.
2482+nthLine :: Int -> Rope -> Rope
2484+ | i < 0 = error $ "Negative line number: " <> show i
2485+ | i == 0 && Rope.rows r == 0 = r
2486+ | i >= Rope.rows r = error $ "Row number out of bounds: " <> show i <> "/" <> show (Rope.rows r)
2487+ | otherwise = Rope.takeWhile (/= '\n') $ fst $ Rope.splitAtLine 1 $ snd $ Rope.splitAtLine (i - 1) r
2489+getWatchedFilesSubscriptionsUntil :: forall end . (FromJSON end, Typeable end) => Session [Maybe Value]
2490+getWatchedFilesSubscriptionsUntil = do
2491+ msgs <- manyTill (Just <$> message @RegisterCapabilityRequest <|> Nothing <$ anyMessage) (message @end)
1791− return res
2492+ return
2494+ | Just RequestMessage{_params = RegistrationParams (List regs)} <- msgs
2495+ , Registration _id WorkspaceDidChangeWatchedFiles args <- regs