In a unit test, when we tell the server to open "SomeFile.hs" it might also open
"SomeOtherFile.hs" because they both use the same cradle. Then we get
diagnostics for both.
Many of the diagnostics have source "typecheck" or "hlint", not "bios". Don't
wait for "bios" diagnostics or we will be waiting forever and timeout. Instead
we should wait for diagnostics with source "typecheck" or "hlint".
With code action literal support, we should not expect to receive applyEdit
requests after we send off a code action to be performed. If we use
getDocumentEdit we will be waiting forever for such a request, and timeout.
Instead, we should use documentContents to get the changed document.
```haskell
-- >>> foo
--
-- >>> bar
```
After running the plugin on both prompts we end up with this:
```haskell
-- >>> foo
-- foo_output
--
-- >>> bar
-- bar_output
```
instead of
```haskell
-- >>> foo
-- foo_output
-- >>> bar
-- bar_output
```
The tactics plugin was only discovering skolem types present in the hole, rather than anywhere in the hypothesis. #541 gives the following repro:
```haskell
foo :: Monad m => (a -> m b) -> (b -> m c) -> (a -> m c)
foo f g a = _
```
here, the hole has type `_ :: m c`, which means tactics doesn't realize `a` and `b` are skolems, and instead treats them as unifiable variables. Thus the insane solution of `f a`.
Fixes#541
This PR tweaks the scoring metric to heavily penalize not using top-level function arguments when defining functions. Presumably if they were added to the type sig, someone had intention behind it. Note that this doesn't prevent us from deriving const, since we have no better alternatives in that case.
Furthermore, this fixes a bug where recursive calls were added to the jLocalHypothesis rather than jAmbientHypothesis. The former is for locally introduced variables, for which usage is rewarded. The result was that we were accidentally rewarding recursive calls! Instead we'd like to penalize them, so this PR adds a field which counts recursive calls and penalizes them.
Fixes#539
Due to an oversight in how we handle polymorphic class methods (fixed in #537), having an applicative context introduces (*>) into scope, which causes an exponential explosion in the search space. On fast machines it seems like we can get through this test before the timeout, but on CI it's flakey.
This PR allows tactics to use methods from given constraints, meaning it can solve holes like this:
```haskell
showMe :: Show a => a -> String
showMe = _
```
It will not, however, discover instances. So this one *won't* solve:
```haskell
showMe :: Int -> String
showMe = _
```
There's quite a lot of finicky details going on in order to support this. The primary challenge is that our types are running after the typechecker has finished, meaning it's already solved the constraints and inlined their evidence. Our solution is to look up the written polymorphic type and unify it with the final, typechecked type. We can use the polymorphic type to find the theta context, and instantiate every class method in the theta.
In addition, this PR fixes a subtle bug in our unification code, which could cause skolems to unify in some circumstances.
Furthermore, it adds a tie-breaker to the scoring metric to prefer shorter programs.
The `auto` tactic attempts to prune unhelpful branches in order to avoid an exponential blowup of search space. On one these optimizations is to not build a data constructor if it doesn't result in new types to solve. For example, we're trying to avoid the following pathological example:
```haskell
data Tree a = Leaf a | Branch (Tree a) (Tree a)
-- given the following hole:
pureTree :: a -> Tree a
pureTree a = _
-- we DO NOT want to fill it with
pureTree a = Branch _ _
```
The reasoning here is that both goals in `Branch _ _` have type `Tree a`, which is already the type we're trying to solve, so introducing `Branch` doesn't make any progress.
This check is performed in the `splitAuto` tactic, but I got it backwards and it wasn't explicitly tested for. The only code which hit it was `pure @[]` --- but because `[]` doesn't have any subgoals, this hit a vacuous case and flipped the result of the bad logic. Two wrongs made a hard to find bug.
This PR:
1. Fixes the reversed logic in `splitAuto`
2. Has a special case for nullary data constructors, fixing the bug cause by vacuousness.
3. Adds property tests ensuring we can `auto` our way through any permutation of a tuple (which is where we originally noticed the bug)
4. Prevents `unsafeRender` from crashing when `unsafeGlobalDynFlags` is unset, such as during testing.
5. Moves tactic solution tracing into the plugin, so it won't run during tests.
The tactics plugin is a bit stupid when working with infix-defined datacons, both in expressions and patterns. For example it will produce (,) a b and (:) a as rather than the more natural (a, b) and a : as. This PR makes it do the right thing.
The solution is to inspect the data con when building an expression or pattern. Unfortunately tuples are extra special in GHC, so this introduces a special case for tuples, and another for everyday infix things (like list).
There's a bit of annoying fiddling in order to build the infix pattern. The logic is in infixifyPatIfNecessary, which is the only thing I'm not super comfortable with in the diff.
Fixes#468
This PR enhances the "attempt to fill hole" code action, allowing it to implement self-recursive functions. The generated code ensures recursion occurs only on structurally-smaller values, and preserves the positional ordering of homomorphically destructed arguments.
It's clever enough to implement foldr and nontrivial functor instances.
Co-authored-by: TOTBWF <reed.mullanix@calabrio.com>
* Add GitHub workflow for testing
* Add HLS_TEST_EXE env var to control which exe to test
* Pass -j1 flag to tasty when running tests on GitHub
* Enable stack in GitHub CI
* Update cabal
* Add HLS_WRAPPER_TEST_EXE
* Fix cache restore keys
* Try force language server to use utf8 locale
* Use patched hie-bios
* Remove debug print
* Lets find out what the windows locale encoding is
* Give up trying to fix the locale
* Add comment for -j1
* [WIP] Start work on 'auto'
* Get current binding
* Fix currentBindingName for class methods
* Proper impl of getDefiningBindings
* Forgot to checkin bindsites
* [WIP] Update version of refinery
* Bind all tyvars if possible
* WIP: Instantiate polymorphic functions
* Split out fresh type variables when instantiating
* Make everything compile
* Separate out tactics
* oneWayUnifyRule
* [WIP] Add 'TacticState'
* [WIP] Tweak 'unify' and 'unifyOneWayRule'
* [WIP] Start work on skolem tracking
* [WIP] Fix 'checkSkolemUnification'
* Update judgement type
* Add a Context to TacticM
* Don't destruct already destructed
* Remove the internal lib
* Cleanup warnings
* Move debug machinery into *.Debug
* Rip types out of machinery
* Cleanup warnings
* Continue splitting Machinery
* Rip out GHC and Naming
* Get it all compiling
* Stop re-exporting from Machinery
* Split out codegen/rules
* Remove gross/unused tactics
* Make newSubgoal derive from an existing judgement
* remove newJudgement
* Disallow current function from auto
* Cleanup auto
* Stop using the Judgement ctor
* Track pattern value
* Better showAstData
* Get module-scoped funcs
* Split all data constructors
* assumption -> assume
* Don't destruct if there are zero datacons
* Lambda case destruct tactics
* Fix the tests
* Rip out debug stuff since it fails CI
* Tests for lambda case actions
* Golden testing machinery
* Attempt to fill hole
* [WIP] Use 'refinery-0.2.0.0'
* [WIP] Update refinery in all stack.yaml files
* Bump version in cabal file
* Bump cabal index state
* Sort goals by heuristic
* Naming for unit types
* Heuristic for auto
* Penalize holes more
* Naming for unit types
* Give the name "unit" to units
* Fallback names for symbols and punctuation
* Get "good" name for symbolic names
* Update plugins/tactics/src/Ide/Plugin/Tactic/CodeGen.hs
Co-authored-by: wz1000 <zubin@cmi.ac.in>
* Make the TacticState strict
* Make the judgement strict
* Simplify when we use position mapping
* Move bindsites to ghcide
Co-authored-by: Sandy Maguire <sandy@sandymaguire.me>
Co-authored-by: wz1000 <zubin@cmi.ac.in>
* Fixes Eval plugin to treat multilined results properly and supports abbreviation for GHCi commands
* Re-orders Pragmas
* Cosmetic changes
* More correction for LANGUAGE pragma style
* Integrates commenting-out works into `evalGhciLikeCmd`
* More systematic handling of GHCi-like commands
* Human-readable Show for GhciLikeCmdException
* Use `T.strip` instead of `T.stripEnd`
* Implements `:type` command and their tests
* Adds test case for unknown `TcRnExprMode` argument
* Adds test case for unknown command
* Simplifies test case T16
* Corrects mode option
* Adds test-case with defaulting clause inside >>> prompt
* Fixes for redundant imports
* Reorganises import list
* Elaborates on the Exception argument roles
* Adds a test-case for module defaulting, marked as expected failure
* Uses `expectFailBecause` instead
* Implements `:kind!` GHCi command to Eval plugin
* Adds golden tests for `:kind!`
* Removes redundant lines
* Adds `:kind` command that returns kind only without normalisation
* Test cases for `:kind`
* Fixes `Applies file LANGUAGE extensions` to avoid sufferring from ImportLens
* Removes redundant leading space
Use @? and @?= instead, since they give much easier to read error
messages. The `shouldX` functions all just end up dumping the Show
instance of a HSpecFailure or what have you which is really hard to
read. It also doesn't look like hspec-expectations is that actively
maintained anymore
Need to read in those test results as a file, since the CPP preprocessor
trips on those multiline strings
Also use @?= instead of `shouldBe`, since the exception thrown by it
gets caught by tasty and is pretty printed
This also requires a bump to lsp-test to fix a test, and drops the
trick that the wrapper tests used to find the wrapper executable since
it was just confusing
Update ghcide to obtain the GHC lib dir at runtime, rather than at compile time with ghc-paths. This means that the binaries can be moved about since the lib dir is obtained on the fly
Share the exe/main.hs logic between ghcide and hls: the session setup logic which previously took up most of exe/main.hs now resides inside the ghcide library, and is used by both ghcide and hls's executables
Add a --project-ghc-version option to the wrapper which spits out the project's ghc version to stdout. This is useful for the vscode extension which can then use it to download the corresponding version of binary that the wrapper would have otherwise attempted to launch
Make the wrapper check to see if the correct tool is installed beforehand. For example, if it detects a stack project but stack isn't on the path, it will report an error then and there, rather than having hls/ghcide confusingly fail later on. The vscode extension uses this new error message as well to provide a pop up message linking the user to a website to install the missing tool
Remove cabal-helper from the wrapper, so that the implicit cradle logic is the same between ghcide/hls/hls-wrapper
And of course, add a GitHub action workflow that runs whenever a release is created on GitHub that builds static binaries on Linux, and distributable enough binaries on macOS and windows. This is documented a bit more in docs/releases.md
* WIP
* WIP 2
* WIP 3
* WIP 4
* WIP 5
* WIP 6
* WIP 7
* WIP 8
* WIP 9 Use patched hie-bios to get libdir dynamically
* Try building the wrapper
* Try to fix build_wrapper env variable not being picked up
* Try again
* Give up on the env var idea
* Try out static optimised builds?
* Try squashing the working dir
* Woops
* Try squashing the builddir
* Try going into the parent directory
* Radical approach - don't use such a long name for the wrapper
* Use dist-binary flag
* Debug why floskell fails to build on windows
* haskell-language-server => hls on CI
I hate that I have to do this
* Employ extreme path saving measures
* sed time :(
* Try making sed command portable
* Compress artefacts
* Tidy up wrapper logging
* Use version checking logic in hie-bios
* Add documentation on the releases process
* Remove unused code
* Append .exe to windows binaries
* Try out building remaining supported ghc configurations
* Add wrapper tests and update hie-bios
* Use index timestamp that exists on hackage
Fixes warning
* Update hie-bios
* Update hie-bios
* Try building windows jobs on -j1
* Skip windows 8.8.2
* Update ghc-check to use hie-bios runtime ghc libdir
* Upload binaries as an artifact too
* Try flicking on enable-executable-static
I don't expect this to work, puffnfresh has already tried this and had
to fork ghcup
* Fix artifact upload
* Update to latest ghcide and reuse loadSession
* Check if the tool is installed in --project-ghc-version in the wrapper
* Fix wrapper tests by copying to temporary directory
* Try caching
* Tidy up and switch back to cabal helper implicit cradle
* use split sections
* Remove cabal-helper and replace it with hie-bios implicit logic
The cabal-helper cradle was only used by the wrapper for detecting the
project GHC version in the absence of an explicit hie.yaml file, whilst
ghcide itself used the hie-bios implicit cradle logic. This brings the
two in sync so the wrapper should behave more predictably now.
* Undo agpl common stanza change
* Add release number
Co-authored-by: amesgen <amesgen@amesgen.de>
Conclusion is that getDocumentContents is returning junk, doing the idempotent
test manually on vscode works as expected, but ends up with junk at the end of
the file in the test.
2020-03-12 21:11:05.79062259 [ThreadId 38] - Formatter.doFormatting: contents=
"{-# LANGUAGE NoImplicitPrelude #-}
module Format where
foo :: Int -> Int
foo 3 = 2
foo x = x
bar :: String -> IO String
bar s = do
x <- return \"hello\"
return \"asdf\"
"
2020-03-12 21:11:07.896114974 [ThreadId 7] - <--2--{"result":[{"range":{"start":{"line":0,"character":0},"end":{"line":9,"character":0}},"newText":
"{-# LANGUAGE NoImplicitPrelude #-}
module Format where
foo :: Int -> Int
foo 3 = 2
foo x = x
bar :: String -> IO String
bar s = do
x <- return \"hello\"
return \"asdf\"
"}],"jsonrpc":"2.0","id":1}
2020-03-12 21:11:07.897123428 [ThreadId 5] - ---> {"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"version":0,"uri":"file:///home/alanz/mysrc/github/alanz/haskell-language-server/test/testdata/Format.hs"},"contentChanges":[{"text":
"{-# LANGUAGE NoImplicitPrelude #-}
module Format where
foo :: Int -> Int
foo 3 = 2
foo x = x
bar :: String -> IO String
bar s = do
x <- return \"hello\"
return \"asdf\"
","range":{"start":{"line":0,"character":0},"end":{"line":9,"character":0}}}]}}
-------------------------------------------------------
2020-03-12 21:11:07.899375044 [ThreadId 213] - Formatter.doFormatting: contents="{-# LANGUAGE NoImplicitPrelude #-}
module Format where
foo :: Int -> Int
foo 3 = 2
foo x = x
bar :: String -> IO String
bar s = do
x <- return \"hello\"
return \"asdf\"
"
2020-03-12 21:11:07.902320214 [ThreadId 7] - <--2--{"result":[{"range":{"start":{"line":0,"character":0},"end":{"line":9,"character":0}},"newText":"{-# LANGUAGE NoImplicitPrelude #-}
module Format where
foo :: Int -> Int
foo 3 = 2
foo x = x
bar :: String -> IO String
bar s = do
x <- return \"hello\"
return \"asdf\"
"}],"jsonrpc":"2.0","id":2}
2020-03-12 21:11:07.902812215 [ThreadId 5] - ---> {"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"version":0,"uri":"file:///home/alanz/mysrc/github/alanz/haskell-language-server/test/testdata/Format.hs"},"contentChanges":[{"text":
"{-# LANGUAGE NoImplicitPrelude #-}
module Format where
foo :: Int -> Int
foo 3 = 2
foo x = x
bar :: String -> IO String
bar s = do
x <- return \"hello\"
return \"asdf\"
","range":{"start":{"line":0,"character":0},"end":{"line":9,"character":0}}}]}}
--------------------------------
hieCommand: haskell-language-server --lsp -d -l test-logs/hie-stack-8.6.5.yaml.log
HIE cache is warmed up
Format
formatting provider
formatting is idempotent FAILED [1]
Failures:
test/functional/FormatSpec.hs:64:42:
1) Format, formatting provider, formatting is idempotent
expected: "{-# LANGUAGE NoImplicitPrelude #-}\n\nmodule Format where\n\nfoo :: Int -> Int\nfoo 3 = 2\nfoo x = x\n\nbar :: String -> IO String\nbar s = do\n x <- return \"hello\"\n return \"asdf\"\n"
but got: "{-# LANGUAGE NoImplicitPrelude #-}\n\nmodule Format where\n\nfoo :: Int -> Int\nfoo 3 = 2\nfoo x = x\n\nbar :: String -> IO String\nbar s = do\n x <- return \"hello\"\n return \"asdf\"\nbar s = do\n x <- return \"hello\"\n return \"asdf\"\n"
To rerun use: --match "/Format/formatting provider/formatting is idempotent/"
Randomized with seed 1814425400
We only have an Ormolu formatter at present, want to make sure the circle stuff
actually works too.
Need to plumb the config values through to the actual formatter.