-
-
Notifications
You must be signed in to change notification settings - Fork 401
Local binding hints (WIP) #4610
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
ba2d544
7bf07ca
51ef231
cd34e8c
c9daf86
232e6ba
f401ff2
287f7fa
228d669
52ea29f
e61663d
8b6f15e
e544187
5fa456d
561d082
b226661
837ee45
96417e0
20e1274
6d5f4af
efc434b
d065a0a
2d221d8
953db22
0df54d2
59d56bc
0e69f46
2f27e9a
80184f8
94c6841
9cde704
330cbe9
d2d8633
a2759f6
78df62b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
module Infix where | ||
|
||
f :: a | ||
f = undefined | ||
where | ||
g :: p1 -> p -> p1 | ||
a `g` b = a |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
module Infix where | ||
|
||
f :: a | ||
f = undefined | ||
where | ||
a `g` b = a |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
module Inline where | ||
|
||
f :: a | ||
f = undefined | ||
where g :: Bool | ||
g = True |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
module Inline where | ||
|
||
f :: a | ||
f = undefined | ||
where g = True |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
module Nest where | ||
|
||
f :: Int | ||
f = g | ||
where | ||
g :: Int | ||
g = h | ||
h :: Int | ||
h = k where k :: Int | ||
k = 3 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
module Nest where | ||
|
||
f :: Int | ||
f = g | ||
where | ||
g = h | ||
h = k where k = 3 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
module NoLens where | ||
|
||
f :: a | ||
f = undefined | ||
where | ||
g = 3 | ||
|
||
|
||
|
||
|
||
|
||
|
||
g :: Int |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
module NoLens where | ||
|
||
f :: a | ||
f = undefined | ||
where | ||
g = 3 | ||
|
||
|
||
|
||
|
||
|
||
|
||
g :: Int |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
module Operator where | ||
|
||
f :: a | ||
f = undefined | ||
where | ||
g :: (a -> b) -> a -> b | ||
g = ($) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
module Operator where | ||
|
||
f :: a | ||
f = undefined | ||
where | ||
g = ($) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
module Qualified where | ||
|
||
import qualified Data.Map as Map | ||
|
||
f :: a | ||
f = undefined | ||
where | ||
g :: Map.Map Bool Char | ||
g = Map.singleton True 'c' |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
module Qualified where | ||
|
||
import qualified Data.Map as Map | ||
|
||
f :: a | ||
f = undefined | ||
where | ||
g = Map.singleton True 'c' |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
{-# LANGUAGE ExplicitForAll #-} | ||
module ScopedTypeVariables where | ||
|
||
f :: forall a b. a -> b -> (a, b) | ||
f aa bb = (aa, ida bb) | ||
where | ||
ida :: b -> b | ||
ida = id |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
{-# LANGUAGE ExplicitForAll #-} | ||
module ScopedTypeVariables where | ||
|
||
f :: forall a b. a -> b -> (a, b) | ||
f aa bb = (aa, ida bb) | ||
where | ||
ida = id |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
module Simple where | ||
|
||
f :: a | ||
f = undefined | ||
where | ||
g :: Bool | ||
g = True |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
module Simple where | ||
|
||
f :: a | ||
f = undefined | ||
where | ||
g = True |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
module Typle where | ||
|
||
f :: a | ||
f = undefined | ||
where | ||
g :: Integer | ||
h :: Bool | ||
(g, h) = (1, True) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
module Typle where | ||
|
||
f :: a | ||
f = undefined | ||
where | ||
(g, h) = (1, True) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
module Typeclass where | ||
|
||
f :: a | ||
f = undefined | ||
where | ||
g :: Num a => a -> a -> a | ||
g a b = a + b |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
module Typeclass where | ||
|
||
f :: a | ||
f = undefined | ||
where | ||
g a b = a + b |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,181 @@ | ||
{-# LANGUAGE ExplicitNamespaces #-} | ||
|
||
module InlayHintTests (tests) where | ||
|
||
import Config (mkIdeTestFs, testWithDummyPlugin, | ||
testWithDummyPluginEmpty) | ||
import Control.Monad (void) | ||
import Control.Monad.IO.Class (MonadIO (liftIO)) | ||
import qualified Data.Aeson as A | ||
import Data.Maybe (mapMaybe) | ||
import qualified Data.Text as T | ||
import Language.LSP.Protocol.Types (InlayHint (..), | ||
Position (Position), | ||
Range (Range, _end, _start), | ||
TextDocumentIdentifier (TextDocumentIdentifier), | ||
TextEdit (TextEdit, _newText, _range), | ||
UInt, | ||
VersionedTextDocumentIdentifier (_uri), | ||
type (|?) (..)) | ||
import Language.LSP.Test (applyEdit, createDoc, | ||
documentContents, getInlayHints, | ||
openDoc, setConfigSection) | ||
import Test.Hls (Assertion, Session, expectFail, | ||
waitForTypecheck) | ||
import Test.Hls.FileSystem (copyDir) | ||
import Test.Tasty (TestTree, testGroup) | ||
import Test.Tasty.HUnit ((@=?), (@?=)) | ||
|
||
tests :: TestTree | ||
tests = testGroup "inlay hints" | ||
[ whereInlayHintsTests | ||
] | ||
|
||
whereInlayHintsTests :: TestTree | ||
whereInlayHintsTests = testGroup "add signature for where clauses" | ||
[ testWithDummyPluginEmpty "No where inlay hints if disabled" $ do | ||
let content = T.unlines | ||
[ "module Sigs where" | ||
, "f :: b" | ||
, "f = undefined" | ||
, " where" | ||
, " g = True" | ||
] | ||
range = Range { _start = Position 4 0 | ||
, _end = Position 4 1000 | ||
} | ||
doc <- createDoc "Sigs.hs" "haskell" content | ||
setConfigSection "haskell" (createConfig False) | ||
inlayHints <- getInlayHints doc range | ||
liftIO $ length inlayHints @?= 0 | ||
, testGroup "apply EditText" | ||
[ editTest "Simple" | ||
, editTest "Tuple" | ||
, editTest "Inline" | ||
, editTest "Infix" | ||
, editTest "Operator" | ||
, expectFail $ editTest "ScopedTypeVariables" | ||
, editTest "Nest" | ||
, editTest "NoLens" | ||
, expectFail $ editTest "Typeclass" | ||
, editTest "Qualified" | ||
] | ||
, testGroup "apply EditText" | ||
[ hintTest "Simple" $ (@=?) | ||
[defInlayHint { _position = Position 5 9 | ||
, _label = InL ":: Bool" | ||
, _textEdits = Just [mkTextEdit 5 8 "g :: Bool\n "] | ||
}] | ||
, hintTest "Tuple" $ (@=?) | ||
[ defInlayHint { _position = Position 5 10 | ||
, _label = InL ":: Integer" | ||
, _textEdits = Just [mkTextEdit 5 8 "g :: Integer\n "] | ||
} | ||
, defInlayHint { _position = Position 5 13 | ||
, _label = InL ":: Bool" | ||
, _textEdits = Just [mkTextEdit 5 8 "h :: Bool\n "] | ||
} | ||
] | ||
, hintTest "Inline" $ (@=?) | ||
[defInlayHint { _position = Position 4 11 | ||
, _label = InL ":: Bool" | ||
, _textEdits = Just [mkTextEdit 4 10 "g :: Bool\n "] | ||
}] | ||
, hintTest "Infix" $ (@=?) | ||
[defInlayHint { _position = Position 5 13 | ||
, _label = InL ":: p1 -> p -> p1" | ||
, _textEdits = Just [mkTextEdit 5 8 "g :: p1 -> p -> p1\n "] | ||
}] | ||
, hintTest "Operator" $ (@=?) | ||
[defInlayHint { _position = Position 5 9 | ||
, _label = InL ":: (a -> b) -> a -> b" | ||
, _textEdits = Just [mkTextEdit 5 8 "g :: (a -> b) -> a -> b\n "] | ||
}] | ||
, hintTest "Nest" $ (@=?) | ||
[ defInlayHint { _position = Position 6 9 | ||
, _label = InL ":: Int" | ||
, _textEdits = Just [mkTextEdit 6 8 "h :: Int\n "] | ||
} | ||
, defInlayHint { _position = Position 5 9 | ||
, _label = InL ":: Int" | ||
, _textEdits = Just [mkTextEdit 5 8 "g :: Int\n "] | ||
} | ||
, defInlayHint { _position = Position 6 21 | ||
, _label = InL ":: Int" | ||
, _textEdits = Just [mkTextEdit 6 20 "k :: Int\n "] | ||
} | ||
] | ||
, hintTest "NoLens" $ (@=?) [] | ||
, hintTest "Qualified" $ (@=?) | ||
[ defInlayHint { _position = Position 7 10 | ||
, _label = InL ":: Map.Map Bool Char" | ||
, _textEdits = Just [mkTextEdit 7 9 "g :: Map.Map Bool Char\n "] | ||
} | ||
] | ||
] | ||
] | ||
|
||
editTest :: String -> TestTree | ||
editTest file = | ||
testWithDummyPlugin (file <> " (InlayHint EditText)") (mkIdeTestFs [copyDir "local-sig-inlay-hints"]) $ do | ||
doc <- openDoc (file ++ ".hs") "haskell" | ||
setConfigSection "haskell" (createConfig True) | ||
executeAllHints doc globalRange | ||
real <- documentContents doc | ||
expectedDoc <- openDoc (file ++ ".expected.hs") "haskell" | ||
expected <- documentContents expectedDoc | ||
liftIO $ real @?= expected | ||
|
||
hintTest :: String -> ([InlayHint] -> Assertion) -> TestTree | ||
hintTest file assert = | ||
testWithDummyPlugin (file <> " (InlayHint)") (mkIdeTestFs [copyDir "local-sig-inlay-hints"]) $ do | ||
doc <- openDoc (file ++ ".hs") "haskell" | ||
setConfigSection "haskell" (createConfig True) | ||
hints <- getInlayHints doc globalRange | ||
liftIO $ assert hints | ||
|
||
|
||
createConfig :: Bool -> A.Value | ||
createConfig on = | ||
A.object [ "plugin" | ||
A..= A.object [ "ghcide-type-lenses" | ||
A..= A.object [ "config" | ||
A..= A.object [ "localBindingInlayHintOn" A..= A.Bool on ]]]] | ||
|
||
|
||
executeAllHints :: TextDocumentIdentifier -> Range -> Session () | ||
executeAllHints doc range = do | ||
void $ waitForTypecheck doc | ||
hints <- getInlayHints doc range | ||
let edits = concat $ mapMaybe _textEdits hints | ||
case edits of | ||
[] -> pure () | ||
edit : _ -> do | ||
newDoc <- applyEdit doc edit | ||
executeAllHints (TextDocumentIdentifier $ _uri newDoc) range | ||
|
||
defInlayHint :: InlayHint | ||
defInlayHint = | ||
InlayHint { _position = Position 0 0 | ||
, _label = InL "" | ||
, _kind = Nothing | ||
, _textEdits = Nothing | ||
, _tooltip = Nothing | ||
, _paddingLeft = Just True | ||
, _paddingRight = Nothing | ||
, _data_ = Nothing | ||
} | ||
|
||
mkTextEdit :: UInt -> UInt -> T.Text -> TextEdit | ||
mkTextEdit x y text = | ||
TextEdit { _range = pointRange x y | ||
, _newText = text | ||
} | ||
|
||
pointRange :: UInt -> UInt -> Range | ||
pointRange x y = Range (Position x y) (Position x y) | ||
|
||
globalRange :: Range | ||
globalRange = Range { _start = Position 0 0 | ||
, _end = Position 1000 0 | ||
} |
Large diffs are not rendered by default.
Unchanged files with check annotations Beta
{ source_version = ver | ||
, old_value = m_old | ||
, get_file_version = use GetModificationTime_{missingFileDiagnostics = False} | ||
, get_linkable_hashes = \fs -> map (snd . fromJust . hirCoreFp) <$> uses_ GetModIface fs | ||
Check warning on line 805 in ghcide/src/Development/IDE/Core/Rules.hs
|
||
, get_module_graph = useWithSeparateFingerprintRule_ GetModuleGraphTransDepsFingerprints GetModuleGraph f | ||
, regenerate = regenerateHiFile session f ms | ||
} |
Just fileDiags -> do | ||
pure $ Just $ filter diagRangeOverlaps fileDiags | ||
where | ||
diagRangeOverlaps = \fileDiag -> | ||
Check warning on line 219 in ghcide/src/Development/IDE/Core/PluginUtils.hs
|
||
rangesOverlap range (fileDiag ^. fdLspDiagnosticL . LSP.range) | ||
-- | Just like 'activeDiagnosticsInRangeMT'. See the docs of 'activeDiagnosticsInRangeMT' for details. |
import Data.Time (UTCTime (..)) | ||
import Data.Tuple.Extra (dupe) | ||
import Debug.Trace | ||
import Development.IDE.Core.FileStore (resetInterfaceStore) | ||
Check warning on line 73 in ghcide/src/Development/IDE/Core/Compile.hs
|
||
import Development.IDE.Core.Preprocessor | ||
import Development.IDE.Core.ProgressReporting (progressUpdate) | ||
import Development.IDE.Core.RuleTypes | ||
tcs = tcg_tcs ts :: [TyCon] | ||
hie_asts = GHC.enrichHie all_binds (tmrRenamed tcm) top_ev_binds insts tcs | ||
pure $ Just $ | ||
#if MIN_VERSION_ghc(9,11,0) | ||
hie_asts (tcg_type_env ts) | ||
#else | ||
convImport (L _ i) = ( | ||
(ideclPkgQual i) | ||
Check warning on line 1106 in ghcide/src/Development/IDE/Core/Compile.hs
|
||
, reLoc $ ideclName i) | ||
msrImports = implicit_imports ++ imps |
{-# LANGUAGE DeriveAnyClass #-} | ||
Check warning on line 1 in ghcide/session-loader/Development/IDE/Session/Diagnostics.hs
|
||
module Development.IDE.Session.Diagnostics where | ||
import Control.Applicative | ||
surround start s end = do | ||
guard (listToMaybe s == Just start) | ||
guard (listToMaybe (reverse s) == Just end) | ||
pure $ drop 1 $ take (length s - 1) s | ||
multiCradleErrMessage :: MultiCradleErr -> [String] | ||
multiCradleErrMessage e = |
[] -> error $ "GHC version could not be parsed: " <> version | ||
((runTime, _):_) | ||
| compileTime == runTime -> do | ||
atomicModifyIORef' cradle_files (\xs -> (cfp:xs,())) | ||
Check warning on line 630 in ghcide/session-loader/Development/IDE/Session.hs
|
||
session (hieYaml, toNormalizedFilePath' cfp, opts, libDir) | ||
| otherwise -> return (([renderPackageSetupException cfp GhcVersionMismatch{..}], Nothing),[]) | ||
-- Failure case, either a cradle error or the none cradle | ||
x <- map errMsgDiagnostic closure_errs | ||
DriverHomePackagesNotClosed us <- pure x | ||
pure us | ||
isBad ci = (homeUnitId_ (componentDynFlags ci)) `OS.member` bad_units | ||
Check warning on line 897 in ghcide/session-loader/Development/IDE/Session.hs
|
||
-- Whenever we spin up a session on Linux, dynamically load libm.so.6 | ||
-- in. We need this in case the binary is statically linked, in which | ||
-- case the interactive session will fail when trying to load |
{-# LANGUAGE CPP #-} | ||
{-# LANGUAGE DataKinds #-} | ||
{-# LANGUAGE LambdaCase #-} | ||
{-# LANGUAGE OverloadedStrings #-} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
regenerate these