Skip to content

Commit 0558c84

Browse files
committed
add completions tests
1 parent d330009 commit 0558c84

File tree

13 files changed

+241
-29
lines changed

13 files changed

+241
-29
lines changed

haskell-language-server.cabal

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,7 @@ test-suite hls-cabal-project-plugin-tests
389389
hs-source-dirs: plugins/hls-cabal-project-plugin/test
390390
main-is: Main.hs
391391
other-modules:
392+
Completer
392393
Utils
393394
build-depends:
394395
, bytestring
@@ -403,6 +404,7 @@ test-suite hls-cabal-project-plugin-tests
403404
, text
404405
, hls-plugin-api
405406
, cabal-install
407+
, haskell-language-server:hls-cabal-plugin
406408

407409

408410
-----------------------------

plugins/hls-cabal-plugin/test/testdata/completer.cabal

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,4 @@ be
1111
library
1212
lib
1313

14-
co
14+
co

plugins/hls-cabal-project-plugin/src/Ide/Plugin/CabalProject/Completion/Data.hs

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,19 @@
22

33
module Ide.Plugin.CabalProject.Completion.Data where
44

5-
import Data.Map (Map)
6-
import qualified Data.Map as Map
7-
import qualified Data.Text as T
8-
import Development.IDE.GHC.Compat.Core (flagsForCompletion)
9-
import Distribution.CabalSpecVersion (CabalSpecVersion (CabalSpecV2_2),
10-
showCabalSpecVersion)
5+
import Data.Map (Map)
6+
import qualified Data.Map as Map
7+
import qualified Data.Text as T
8+
import Development.IDE.GHC.Compat.Core (flagsForCompletion)
9+
import Distribution.CabalSpecVersion (CabalSpecVersion (CabalSpecV2_2),
10+
showCabalSpecVersion)
1111
-- import Ide.Plugin.Cabal.Completion.Completer.FilePath
1212
-- import Ide.Plugin.Cabal.Completion.Completer.Module
1313
-- import Ide.Plugin.Cabal.Completion.Completer.Paths
14+
import Ide.Plugin.Cabal.Completion.Completer.FilePath (directoryCompleter,
15+
filePathCompleter)
1416
import Ide.Plugin.Cabal.Completion.Completer.Simple
15-
import Ide.Plugin.Cabal.Completion.Completer.Types (Completer)
17+
import Ide.Plugin.Cabal.Completion.Completer.Types (Completer)
1618
import Ide.Plugin.Cabal.Completion.Types
1719
-- import Ide.Plugin.Cabal.LicenseSuggest (licenseNames)
1820

@@ -47,12 +49,12 @@ cabalProjectKeywords :: Map KeyWordName Completer
4749
cabalProjectKeywords =
4850
Map.fromList
4951
[ -- projectConfigFieldGrammar
50-
("packages:", noopCompleter),
51-
("optional-packages:", noopCompleter),
52-
("extra-packages:", noopCompleter),
52+
("packages:", filePathCompleter),
53+
("optional-packages:", filePathCompleter),
54+
("extra-packages:", filePathCompleter),
5355
-- projectConfigBuildOnlyFieldGrammar
5456
("verbose:", constantCompleter ["0", "1", "2", "3"]),
55-
("build-summary:", noopCompleter),
57+
("build-summary:", filePathCompleter),
5658
("build-log:", noopCompleter),
5759
("remote-build-reporting:", noopCompleter),
5860
("report-planning-failure:", noopCompleter),
@@ -72,8 +74,8 @@ cabalProjectKeywords =
7274
("project-file:", noopCompleter),
7375
("ignore-project:", noopCompleter),
7476
("compiler:", constantCompleter ["ghc", "ghcjs", "jhc", "lhc", "uhc", "haskell-suite"]),
75-
("with-compiler:", noopCompleter),
76-
("with-hc-pkg:", noopCompleter),
77+
("with-compiler:", filePathCompleter),
78+
("with-hc-pkg:", filePathCompleter),
7779
("doc-index-file:", noopCompleter),
7880
("package-dbs:", noopCompleter),
7981
("active-repositories:", noopCompleter),
@@ -93,23 +95,23 @@ cabalProjectKeywords =
9395
("minimize-conflict-set:", constantCompleter ["False", "True"]),
9496
("strong-flags:", constantCompleter ["False", "True"]),
9597
("allow-boot-library-installs:", constantCompleter ["False", "True"]),
96-
("reject-unconstrained-dependencies:", noopCompleter),
98+
("reject-unconstrained-dependencies:", constantCompleter ["none", "all"]),
9799
("per-component:", noopCompleter),
98100
("independent-goals:", noopCompleter),
99101
("prefer-oldest:", noopCompleter),
100102
("extra-prog-path-shared-only:", noopCompleter),
101103
("multi-repl:", noopCompleter),
102104
-- extras
103-
("benchmarks:", constantCompleter ["False", "True"])
104-
105+
("benchmarks:", constantCompleter ["False", "True"]),
106+
("import:", filePathCompleter)
105107
]
106108

107109
packageFields :: Map KeyWordName Completer
108110
packageFields =
109111
Map.fromList
110112
[ -- packageConfigFieldGrammar
111113
("haddock-all:", constantCompleter ["False", "True"]),
112-
("extra-prog-path:", noopCompleter),
114+
("extra-prog-path:", filePathCompleter),
113115
("flags:", noopCompleter),
114116
("library-vanilla:", constantCompleter ["True", "False"]),
115117
("shared:", constantCompleter ["False", "True"]),
@@ -126,10 +128,10 @@ packageFields =
126128
("optimization:", constantCompleter ["0", "1", "2", "True", "False"]),
127129
("program-prefix:", noopCompleter),
128130
("program-suffix:", noopCompleter),
129-
("extra-lib-dirs:", noopCompleter),
130-
("extra-lib-dirs-static:", noopCompleter),
131-
("extra-framework-dirs:", noopCompleter),
132-
("extra-include-dirs:", noopCompleter),
131+
("extra-lib-dirs:", directoryCompleter),
132+
("extra-lib-dirs-static:", directoryCompleter),
133+
("extra-framework-dirs:", directoryCompleter),
134+
("extra-include-dirs:", directoryCompleter),
133135
("library-for-ghci:", constantCompleter ["True", "False"]),
134136
("split-sections:", constantCompleter ["False", "True"]),
135137
("split-objs:", constantCompleter ["False", "True"]),
@@ -150,10 +152,10 @@ packageFields =
150152
("haddock-tests:", constantCompleter ["False", "True"]),
151153
("haddock-benchmarks:", constantCompleter ["False", "True"]),
152154
("haddock-internal:", constantCompleter ["False", "True"]),
153-
("haddock-css:", noopCompleter),
155+
("haddock-css:", filePathCompleter),
154156
("haddock-hyperlink-source:", constantCompleter ["False", "True"]),
155157
("haddock-quickjump:", noopCompleter),
156-
("haddock-hscolour-css:", noopCompleter),
158+
("haddock-hscolour-css:", filePathCompleter),
157159
("haddock-contents-location:", noopCompleter),
158160
("haddock-index-location:", noopCompleter),
159161
("haddock-base-url:", noopCompleter),
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
{-# LANGUAGE DisambiguateRecordFields #-}
2+
{-# LANGUAGE OverloadedStrings #-}
3+
{-# LANGUAGE QuasiQuotes #-}
4+
5+
6+
module Completer where
7+
8+
import Control.Lens ((^.), (^?))
9+
import Control.Lens.Prism
10+
import Control.Monad (forM_)
11+
import qualified Data.ByteString as ByteString
12+
import qualified Data.ByteString.Char8 as BS8
13+
import Data.Maybe (mapMaybe)
14+
import qualified Data.Text as T
15+
import qualified Development.IDE.Plugin.Completions.Types as Ghcide
16+
import qualified Distribution.Fields as Syntax
17+
import Distribution.PackageDescription (GenericPackageDescription)
18+
import Distribution.PackageDescription.Parsec (parseGenericPackageDescriptionMaybe)
19+
import qualified Distribution.Parsec.Position as Syntax
20+
import Ide.Plugin.Cabal.Completion.Completer.FilePath
21+
import Ide.Plugin.Cabal.Completion.Completer.Module
22+
import Ide.Plugin.Cabal.Completion.Completer.Paths
23+
import Ide.Plugin.Cabal.Completion.Completer.Simple (importCompleter)
24+
import Ide.Plugin.Cabal.Completion.Completer.Types (CompleterData (..))
25+
import Ide.Plugin.Cabal.Completion.Types (CabalPrefixInfo (..),
26+
StanzaName)
27+
import Ide.Plugin.CabalProject.Completion.Completions
28+
import qualified Language.LSP.Protocol.Lens as L
29+
import System.FilePath
30+
import Test.Hls
31+
import Utils
32+
33+
completerTests :: TestTree
34+
completerTests =
35+
testGroup
36+
"Completer Tests"
37+
[ basicCompleterTests,
38+
fileCompleterTests,
39+
filePathCompletionContextTests
40+
-- directoryCompleterTests,
41+
-- completionHelperTests,
42+
-- filePathExposedModulesTests,
43+
-- exposedModuleCompleterTests,
44+
-- importCompleterTests,
45+
-- autogenFieldCompletionTests
46+
]
47+
48+
basicCompleterTests :: TestTree
49+
basicCompleterTests =
50+
testGroup
51+
"Basic Completer Tests"
52+
[ runCabalProjectTestCaseSession "In stanza context - stanza should not be suggested" "" $ do
53+
doc <- openDoc "cabal.completer.project" "cabal-project"
54+
compls <- getCompletions doc (Position 1 4)
55+
let complTexts = getTextEditTexts compls
56+
liftIO $ assertBool "does not suggest packages" $ "packages" `notElem` complTexts
57+
liftIO $ assertBool "suggests program-prefix keyword" $ "program-prefix:" `elem` complTexts
58+
, runCabalProjectTestCaseSession "In top level context - stanza should be suggested" "" $ do
59+
doc <- openDoc "cabal.completer.project" "cabal-project"
60+
compls <- getCompletions doc (Position 5 2)
61+
let complTexts = getTextEditTexts compls
62+
liftIO $ assertBool "suggests package" $ "package" `elem` complTexts
63+
, runCabalProjectTestCaseSession "In top level context - stanza should be suggested" "" $ do
64+
doc <- openDoc "cabal.completer.project" "cabal-project"
65+
compls <- getCompletions doc (Position 3 2)
66+
let complTexts = getTextEditTexts compls
67+
liftIO $ assertBool "suggests program-options" $ "program-options" `elem` complTexts
68+
]
69+
where
70+
getTextEditTexts :: [CompletionItem] -> [T.Text]
71+
getTextEditTexts compls = mapMaybe (^? L.textEdit . _Just . _L . L.newText) compls
72+
73+
fileCompleterTests :: TestTree
74+
fileCompleterTests =
75+
testGroup
76+
"File Completer Tests"
77+
[ testCase "Current Directory - no leading ./ by default" $ do
78+
completions <- completeFilePath "" filePathComplTestDir
79+
completions @?== ["Content.hs", "dir1/", "dir2/", "textfile.txt", "test.cabal", "cabal.project"],
80+
testCase "Current Directory - alternative writing" $ do
81+
completions <- completeFilePath "./" filePathComplTestDir
82+
completions @?== ["./Content.hs", "./dir1/", "./dir2/", "./textfile.txt", "./test.cabal", "./cabal.project"],
83+
testCase "Current Directory - hidden file start" $ do
84+
completions <- completeFilePath "." filePathComplTestDir
85+
completions @?== ["Content.hs", "textfile.txt", "test.cabal", "cabal.project"],
86+
testCase "Current Directory - incomplete directory path written" $ do
87+
completions <- completeFilePath "di" filePathComplTestDir
88+
completions @?== ["dir1/", "dir2/"],
89+
testCase "Current Directory - incomplete filepath written" $ do
90+
completions <- completeFilePath "te" filePathComplTestDir
91+
completions @?== ["Content.hs", "textfile.txt", "test.cabal"],
92+
testCase "Subdirectory" $ do
93+
completions <- completeFilePath "dir1/" filePathComplTestDir
94+
completions @?== ["dir1/f1.txt", "dir1/f2.hs"],
95+
-- testCase "Subdirectory - incomplete filepath written" $ do
96+
-- completions <- completeFilePath "dir2/dir3/MA" filePathComplTestDir
97+
-- completions @?== ["dir2/dir3/MARKDOWN.md"],
98+
testCase "Nonexistent directory" $ do
99+
completions <- completeFilePath "dir2/dir4/" filePathComplTestDir
100+
completions @?== []
101+
]
102+
where
103+
completeFilePath :: T.Text -> TestName -> IO [T.Text]
104+
completeFilePath written dirName = do
105+
completer <- filePathCompleter mempty $ mkCompleterData $ simpleCabalPrefixInfoFromFp written dirName
106+
pure $ fmap extract completer
107+
108+
filePathCompletionContextTests :: TestTree
109+
filePathCompletionContextTests =
110+
testGroup
111+
"File Path Completion Context Tests"
112+
[ testCase "empty file - start" $ do
113+
let complContext = getCabalPrefixInfo "" (simplePosPrefixInfo "" 0 0)
114+
completionPrefix complContext @?= "",
115+
testCase "only whitespaces" $ do
116+
let complContext = getCabalPrefixInfo "" (simplePosPrefixInfo " " 0 3)
117+
completionPrefix complContext @?= "",
118+
testCase "simple filepath" $ do
119+
let complContext = getCabalPrefixInfo "" (simplePosPrefixInfo " src/" 0 7)
120+
completionPrefix complContext @?= "src/",
121+
testCase "simple filepath - starting apostrophe" $ do
122+
let complContext = getCabalPrefixInfo "" (simplePosPrefixInfo " \"src/" 0 8)
123+
completionPrefix complContext @?= "src/",
124+
testCase "simple filepath - starting apostrophe, already closed" $ do
125+
let complContext = getCabalPrefixInfo "" (simplePosPrefixInfo " \"src/\"" 0 8)
126+
completionPrefix complContext @?= "src/",
127+
testCase "second filepath - starting apostrophe" $ do
128+
let complContext = getCabalPrefixInfo "" (simplePosPrefixInfo "fp.txt \"src/" 0 12)
129+
completionPrefix complContext @?= "src/",
130+
testCase "middle filepath - starting apostrophe" $ do
131+
let complContext = getCabalPrefixInfo "" (simplePosPrefixInfo "fp.txt \"src/ fp2.txt" 0 12)
132+
completionPrefix complContext @?= "src/",
133+
testCase "middle filepath - starting apostrophe, already closed" $ do
134+
let complContext = getCabalPrefixInfo "" (simplePosPrefixInfo "fp.t xt \"src\" fp2.txt" 0 12)
135+
completionPrefix complContext @?= "src",
136+
testCase "middle filepath - starting apostrophe, already closed" $ do
137+
let complContext = getCabalPrefixInfo "" (simplePosPrefixInfo "\"fp.txt\" \"src fp2.txt" 0 13)
138+
completionPrefix complContext @?= "src",
139+
testCase "Current Directory" $ do
140+
compls <-
141+
listFileCompletions
142+
mempty
143+
PathCompletionInfo
144+
{ isStringNotationPath = Nothing,
145+
pathSegment = "",
146+
queryDirectory = "",
147+
workingDirectory = filePathComplTestDir
148+
}
149+
compls @?== ["Content.hs", "dir1/", "dir2/", "textfile.txt", "test.cabal", "cabal.project"],
150+
testCase "In directory" $ do
151+
compls <-
152+
listFileCompletions
153+
mempty
154+
PathCompletionInfo
155+
{ isStringNotationPath = Nothing,
156+
pathSegment = "",
157+
queryDirectory = "dir1/",
158+
workingDirectory = filePathComplTestDir
159+
}
160+
compls @?== ["f1.txt", "f2.hs"]
161+
]
162+
where
163+
simplePosPrefixInfo :: T.Text -> UInt -> UInt -> Ghcide.PosPrefixInfo
164+
simplePosPrefixInfo lineString linePos charPos =
165+
Ghcide.PosPrefixInfo
166+
{ Ghcide.fullLine = lineString,
167+
Ghcide.prefixScope = "",
168+
Ghcide.prefixText = "",
169+
Ghcide.cursorPos = Position linePos charPos
170+
}
171+
172+
mkCompleterData :: CabalPrefixInfo -> CompleterData
173+
mkCompleterData prefInfo = CompleterData {getLatestGPD = undefined, cabalPrefixInfo = prefInfo, stanzaName = Nothing}
174+
175+
extract :: CompletionItem -> T.Text
176+
extract item = case item ^. L.textEdit of
177+
Just (InL v) -> v ^. L.newText
178+
_ -> error ""

plugins/hls-cabal-project-plugin/test/Main.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ module Main (
66
main,
77
) where
88

9+
import Completer (completerTests)
910
import qualified Control.Exception as E
1011
import Control.Lens ((^.))
1112
import Control.Lens.Fold ((^?))
@@ -36,6 +37,7 @@ main = do
3637
"Cabal Plugin Tests"
3738
[ unitTests
3839
, pluginTests
40+
, completerTests
3941
]
4042

4143
-- ------------------------------------------------------------------------

plugins/hls-cabal-project-plugin/test/Utils.hs

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,19 @@
44

55
module Utils where
66

7-
import Control.Monad (guard)
8-
import Data.List (sort)
9-
import Data.Proxy (Proxy (Proxy))
10-
import qualified Data.Text as T
11-
import Ide.Plugin.CabalProject (descriptor)
7+
import Control.Monad (guard)
8+
import Data.List (sort)
9+
import Data.Proxy (Proxy (Proxy))
10+
import qualified Data.Text as T
11+
import Ide.Plugin.Cabal.Completion.Types
12+
import Ide.Plugin.CabalProject (descriptor)
1213
import qualified Ide.Plugin.CabalProject
1314
import Ide.Plugin.CabalProject.Types
1415
import System.FilePath
1516
import Test.Hls
1617

1718

19+
1820
cabalProjectPlugin :: PluginTestDescriptor Ide.Plugin.CabalProject.Log
1921
cabalProjectPlugin = mkPluginTestDescriptor descriptor "cabal-project"
2022

@@ -46,3 +48,19 @@ cabalProjectCaptureKick = captureKickDiagnostics cabalProjectKickStart cabalProj
4648
-- | list comparison where the order in the list is irrelevant
4749
(@?==) :: (HasCallStack, Ord a, Show a) => [a] -> [a] -> Assertion
4850
(@?==) l1 l2 = sort l1 @?= sort l2
51+
52+
-- potentially add these as imports?
53+
simpleCabalPrefixInfoFromFp :: T.Text -> FilePath -> CabalPrefixInfo
54+
simpleCabalPrefixInfoFromFp prefix fp =
55+
CabalPrefixInfo
56+
{ completionPrefix = prefix
57+
, isStringNotation = Nothing
58+
, completionCursorPosition = Position 0 0
59+
, completionRange = Range (Position 0 0) (Position 0 0)
60+
, completionWorkingDir = fp
61+
, completionFileName = "test"
62+
}
63+
64+
filePathComplTestDir :: FilePath
65+
filePathComplTestDir = addTrailingPathSeparator $ testDataDir </> "filepath-completions"
66+
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package Cabal
2+
pa
3+
4+
pr
5+
6+
pa

plugins/hls-cabal-project-plugin/test/testdata/filepath-completions/Content.hs

Whitespace-only changes.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
packages: ./
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
test text file

0 commit comments

Comments
 (0)