on 0992bc7 help sign in
77 files changed+1746−8961
since splitsnapshots
unifiedsplit
.azure/linux-stack.yml−63 .azure/windows-stack.yml−68 .editorconfig−11 .ghci+24 .gitignore−4 .hlint.yaml+3−7 CHANGELOG.md−35 README.md+3−62 azure-pipelines.yml+184−3 bench/Main.hs−287 docs/Setup.md+1−38 exe/Arguments.hs−4 exe/Main.hs+79−546 exe/Utils.hs−9 fmt.sh+1−1 ghcide.cabal+23−120 hie.yaml+1−12 src-ghc810/Development/IDE/GHC/HieAst.hs−1928 src-ghc88/Development/IDE/GHC/HieAst.hs−1789 src/Development/IDE/Compat.hs−19 src/Development/IDE/Core/Compile.hs+104−306 src/Development/IDE/Plugin/Completions/Logic.hs → src/Development/IDE/Core/Completions.hs+49−45 src/Development/IDE/Plugin/Completions/Types.hs → src/Development/IDE/Core/CompletionsTypes.hs+2−2 src/Development/IDE/Core/Debouncer.hs+11−18 src/Development/IDE/Core/FileExists.hs+46−40 src/Development/IDE/Core/FileStore.hs+36−13 src/Development/IDE/Core/IdeConfiguration.hs−71 src/Development/IDE/Core/OfInterest.hs+24−24 src/Development/IDE/Core/PositionMapping.hs+10−48 src/Development/IDE/Core/Preprocessor.hs+6−16 src/Development/IDE/Core/RuleTypes.hs+18−47 src/Development/IDE/Core/Rules.hs+136−421 src/Development/IDE/Core/Service.hs+1−4 src/Development/IDE/Core/Shake.hs+127−148 src/Development/IDE/GHC/CPP.hs+4−27 src/Development/IDE/GHC/Compat.hs+33−188 src/Development/IDE/GHC/Error.hs+4−28 src/Development/IDE/GHC/Orphans.hs+3−10 src/Development/IDE/GHC/Util.hs+65−146 src/Development/IDE/GHC/WithDynFlags.hs−30 src/Development/IDE/Import/DependencyInformation.hs+34−121 src/Development/IDE/Import/FindImports.hs+17−63 src/Development/IDE/Plugin/CodeAction.hs → src/Development/IDE/LSP/CodeAction.hs+112−177 src/Development/IDE/LSP/Completions.hs+51 src/Development/IDE/LSP/HoverDefinition.hs+6−9 src/Development/IDE/LSP/LanguageServer.hs+32−54 src/Development/IDE/LSP/Notifications.hs+5−14 src/Development/IDE/LSP/Outline.hs+10−49 src/Development/IDE/LSP/Server.hs+10−11 src/Development/IDE/Plugin.hs−60 src/Development/IDE/Plugin/CodeAction/PositionIndexed.hs−112 src/Development/IDE/Plugin/CodeAction/RuleTypes.hs−61 src/Development/IDE/Plugin/CodeAction/Rules.hs−61 src/Development/IDE/Plugin/Completions.hs−103 src/Development/IDE/Spans/AtPoint.hs+18−17 src/Development/IDE/Spans/Calculate.hs+27−65 src/Development/IDE/Spans/Common.hs+13−15 src/Development/IDE/Spans/Documentation.hs+19−21 src/Development/IDE/Types/Diagnostics.hs+5−19 src/Development/IDE/Types/Location.hs+79−24 src/Development/IDE/Types/Options.hs+8−35 stack-ghc-lib.yaml+4−7 stack.yaml+4−8 stack810.yaml−18 stack84.yaml+6−19 stack88.yaml+3−6 test/data/hover/GotoHover.hs → test/data/GotoHover.hs−7 test/data/hover/Bar.hs−3 test/data/hover/Foo.hs−5 test/data/multi/a/A.hs−3 test/data/multi/a/a.cabal−9 test/data/multi/b/B.hs−3 test/data/multi/b/b.cabal−9 test/data/multi/cabal.project−1 test/data/multi/hie.yaml−6 test/exe/Main.hs+283−1120 test/src/Development/IDE/Test.hs+2−8
.azure/linux-stack.ymldeleted−63
@@ -1,63 +0,0 @@
1−jobs:
2−- job: ghcide_stack_linux
3− timeoutInMinutes: 60
4− pool:
5− vmImage: 'ubuntu-latest'
6− strategy:
7− matrix:
8− stack_810:
9− STACK_YAML: "stack810.yaml"
10− stack_88:
11− STACK_YAML: "stack88.yaml"
12− stack_86:
13− STACK_YAML: "stack.yaml"
14− stack_84:
15− STACK_YAML: "stack84.yaml"
16− stack_ghc_lib_88:
17− STACK_YAML: "stack-ghc-lib.yaml"
18− steps:
19− - checkout: self
20− - task: Cache@2
21− inputs:
22− key: stack-cache-v2 | $(Agent.OS) | $(Build.SourcesDirectory)/$(STACK_YAML) | $(Build.SourcesDirectory)/ghcide.cabal
23− path: .azure-cache
24− cacheHitVar: CACHE_RESTORED
25− displayName: "Cache stack artifacts"
26− - bash: |
27− mkdir -p ~/.stack
28− tar xzf .azure-cache/stack-root.tar.gz -C $HOME
29− displayName: "Unpack cache"
30− condition: eq(variables.CACHE_RESTORED, 'true')
31− - bash: |
32− ./fmt.sh
33− displayName: "HLint via ./fmt.sh"
34− - bash: |
35− sudo add-apt-repository ppa:hvr/ghc
36− sudo apt-get update
37− sudo apt-get install -y g++ gcc libc6-dev libffi-dev libgmp-dev make zlib1g-dev cabal-install-3.2
38− if ! which stack >/dev/null 2>&1; then
39− curl -sSL https://get.haskellstack.org/ | sh
40− fi
41− displayName: 'Install Stack'
42− - bash: stack setup --stack-yaml=$STACK_YAML
43− displayName: 'stack setup'
44− - bash: stack build --only-dependencies --stack-yaml=$STACK_YAML
45− displayName: 'stack build --only-dependencies'
46− - bash: |
47− export PATH=/opt/cabal/bin:$PATH
48− stack test --ghc-options=-Werror --stack-yaml=$STACK_YAML || stack test --ghc-options=-Werror --stack-yaml=$STACK_YAML|| stack test --ghc-options=-Werror --stack-yaml=$STACK_YAML
49− # ghcide stack tests are flaky, see https://github.com/digital-asset/daml/issues/2606.
50− displayName: 'stack test --ghc-options=-Werror'
51− - bash: |
52− mkdir -p .azure-cache
53− tar czf .azure-cache/stack-root.tar.gz -C $HOME .stack
54− displayName: "Pack cache"
55− - bash: |
56− set -euo pipefail
57− MESSAGE=$(git log --pretty=format:%s -n1)
58− curl -XPOST \
59− -i \
60− -H 'Content-type: application/json' \
61− --data "{\"text\":\"<!here> *FAILED* $(Agent.JobName): <https://dev.azure.com/digitalasset/ghcide/_build/results?buildId=$(Build.BuildId)|$MESSAGE>\n\"}" \
62− $(Slack.URL)
63− condition: and(failed(), eq(variables['Build.SourceBranchName'], 'master'))
.azure/windows-stack.ymldeleted−68
@@ -1,68 +0,0 @@
1−jobs:
2−- job: ghcide_stack_windows
3− timeoutInMinutes: 60
4− pool:
5− vmImage: 'windows-2019'
6− strategy:
7− matrix:
8− stack_810:
9− STACK_YAML: "stack810.yaml"
10− stack_88:
11− STACK_YAML: "stack88.yaml"
12− stack_86:
13− STACK_YAML: "stack.yaml"
14− stack_84:
15− STACK_YAML: "stack84.yaml"
16− stack_ghc_lib_88:
17− STACK_YAML: "stack-ghc-lib.yaml"
18− variables:
19− STACK_ROOT: "C:\\sr"
20− steps:
21− - checkout: self
22− - task: Cache@2
23− inputs:
24− key: stack-cache-v2 | $(Agent.OS) | $(Build.SourcesDirectory)/$(STACK_YAML) | $(Build.SourcesDirectory)/ghcide.cabal
25− path: .azure-cache
26− cacheHitVar: CACHE_RESTORED
27− displayName: "Cache stack artifacts"
28− - bash: |
29− mkdir -p $STACK_ROOT
30− tar -vxzf .azure-cache/stack-root.tar.gz -C /c
31− mkdir -p .stack-work
32− tar -vxzf .azure-cache/stack-work.tar.gz
33− displayName: "Unpack cache"
34− condition: eq(variables.CACHE_RESTORED, 'true')
35− - bash: |
36− ./fmt.sh
37− displayName: "HLint via ./fmt.sh"
38− - bash: |
39− curl -sSkL http://www.stackage.org/stack/windows-x86_64 -o /usr/bin/stack.zip
40− unzip -o /usr/bin/stack.zip -d /usr/bin/
41− mkdir -p "$STACK_ROOT"
42− displayName: 'Install Stack'
43− - bash: stack setup --stack-yaml $STACK_YAML
44− displayName: 'stack setup'
45− - bash: |
46− # Installing happy and alex standalone to avoid error "strip.exe: unable to rename ../*.exe; reason: File exists"
47− stack install happy --stack-yaml $STACK_YAML
48− stack install alex --stack-yaml $STACK_YAML
49− stack install cabal-install --stack-yaml $STACK_YAML
50− stack build --only-dependencies --stack-yaml $STACK_YAML
51− displayName: 'stack build --only-dependencies'
52− - bash: stack test --no-run-tests --ghc-options=-Werror --stack-yaml $STACK_YAML
53− displayName: 'stack test --ghc-options=-Werror'
54− # TODO: run test suite when failing tests are fixed or marked as broken. See https://github.com/digital-asset/ghcide/issues/474
55− - bash: |
56− mkdir -p .azure-cache
57− tar -vczf .azure-cache/stack-root.tar.gz $(cygpath $STACK_ROOT)
58− tar -vczf .azure-cache/stack-work.tar.gz .stack-work
59− displayName: "Pack cache"
60− - bash: |
61− set -euo pipefail
62− MESSAGE=$(git log --pretty=format:%s -n1)
63− curl -XPOST \
64− -i \
65− -H 'Content-type: application/json' \
66− --data "{\"text\":\"<!here> *FAILED* $(Agent.JobName): <https://dev.azure.com/digitalasset/ghcide/_build/results?buildId=$(Build.BuildId)|$MESSAGE>\n\"}" \
67− $(Slack.URL)
68− condition: and(failed(), eq(variables['Build.SourceBranchName'], 'master'))
.editorconfigdeleted−11
@@ -1,11 +0,0 @@
1−; This file is for unifying the coding style for different editors and IDEs.
2−; More information at https://EditorConfig.org
3−
4−root = true
5−
6−[*]
7−end_of_line = LF
8−indent_style = space
9−indent_size = 4
10−trim_trailing_whitespace = true
11−insert_final_newline = true
.ghcinew file+24
@@ -0,0 +1,24 @@
1+:set -Wunused-binds -Wunused-imports -Worphans -Wunused-matches -Wincomplete-patterns
2+
3+:set -XBangPatterns
4+:set -XDeriveFunctor
5+:set -XDeriveGeneric
6+:set -XGeneralizedNewtypeDeriving
7+:set -XLambdaCase
8+:set -XNamedFieldPuns
9+:set -XOverloadedStrings
10+:set -XRecordWildCards
11+:set -XScopedTypeVariables
12+:set -XStandaloneDeriving
13+:set -XTupleSections
14+:set -XTypeApplications
15+:set -XViewPatterns
16+
17+:set -package=ghc
18+:set -ignore-package=ghc-lib-parser
19+:set -DGHC_STABLE
20+:set -Iinclude
21+:set -isrc
22+:set -iexe
23+
24+:load Main
.gitignore−4
@@ -4,7 +4,3 @@ dist-newstyle/
44cabal.project.local
55*~
66*.lock
7−/.tasty-rerun-log
8−.vscode
9−/.hlint-*
10−bench/example
.hlint.yaml+3−7
@@ -73,23 +73,19 @@
7373 - {name: ViewPatterns, within: []}
7474
7575 # Shady extensions
76+ - {name: ImplicitParams, within: []}
7677 - name: CPP
7778 within:
78− - Development.IDE.Compat
79+ - Development.IDE.Core.Completions
7980 - Development.IDE.Core.FileStore
8081 - Development.IDE.Core.Compile
81− - Development.IDE.Core.Rules
8282 - Development.IDE.GHC.Compat
83− - Development.IDE.GHC.Orphans
8483 - Development.IDE.GHC.Util
8584 - Development.IDE.Import.FindImports
86− - Development.IDE.LSP.Outline
85+ - Development.IDE.LSP.CodeAction
8786 - Development.IDE.Spans.Calculate
8887 - Development.IDE.Spans.Documentation
8988 - Development.IDE.Spans.Common
90− - Development.IDE.Plugin.CodeAction
91− - Development.IDE.Plugin.Completions
92− - Development.IDE.Plugin.Completions.Logic
9389 - Main
9490
9591- flags:
CHANGELOG.md−35
@@ -1,40 +1,5 @@
11### unreleased
22
3−### 0.2.0 (2020-06-02)
4−
5−* Multi-component support (thanks @mpickering)
6−* Support for GHC 8.10 (thanks @sheaf and @chshersh)
7−* Fix some TH issues (thanks @mpickering)
8−* Automatically pick up changes to cradle dependencies (e.g. cabal
9− files) (thanks @jinwoo)
10−* Track dependencies when using `qAddDependentFile` (thanks @mpickering)
11−* Add record fields to document symbols outline (thanks @bubba)
12−* Fix some space leaks (thanks @mpickering)
13−* Strip redundant path information from diagnostics (thanks @tek)
14−* Fix import suggestions for operators (thanks @eddiemundo)
15−* Significant reductions in memory usage by using interfaces and `.hie` files (thanks
16− @pepeiborra)
17−* Minor improvements to completions
18−* More comprehensive suggestions for missing imports (thanks @pepeiborra)
19−* Group imports in document outline (thanks @fendor)
20−* Upgrade to haskell-lsp-0.22 (thanks @bubba)
21−* Upgrade to hie-bios 0.5 (thanks @fendor)
22−
23−### 0.1.0 (2020-02-04)
24−
25−* Code action for inserting new definitions (see #309).
26−* Better default GC settings (see #329 and #333).
27−* Various performance improvements (see #322 and #384).
28−* Improvements to hover information (see #317 and #338).
29−* Support GHC 8.8.2 (see #355).
30−* Include keywords in completions (see #351).
31−* Fix some issues with aborted requests (see #353).
32−* Use hie-bios 0.4.0 (see #382).
33−* Avoid stuck progress reporting (see #400).
34−* Only show progress notifications after 0.1s (see #392).
35−* Progress reporting is now in terms of the number of files rather
36− than the number of shake rules (see #379).
37−
383### 0.0.6 (2020-01-10)
394
405* Fix type in hover information for do-notation and list
README.md+3−62
@@ -25,40 +25,13 @@ There are more details about our approach [in this blog post](https://4ta.uk/p/s
2525| Display type and source module of values | hover |
2626| Remove redundant imports, replace suggested typos for values and module imports, fill type holes, insert missing type signatures, add suggested ghc extensions | codeAction (quickfix) |
2727
28−
29−## Limitations to Multi-Component support
30−
31−`ghcide` supports loading multiple components into the same session so that
32−features such as go-to definition work across components. However, there are
33−some limitations to this.
34−
35−1. You will get much better results currently manually specifying the hie.yaml file.
36−Until tools like cabal and stack provide the right interface to support multi-component
37−projects, it is always advised to specify explicitly how your project partitions.
38−2. Cross-component features only work if you have loaded at least one file
39−from each component.
40−3. There is a known issue where if you have three components, such that A depends on B which depends on C
41−then if you load A and C into the session but not B then under certain situations you
42−can get strange errors about a type coming from two different places. See [this repo](https://github.com/fendor/ghcide-bad-interface-files) for
43−a simple reproduction of the bug.
44−
4528## Using it
4629
4730### Install `ghcide`
4831
4932#### With Nix
5033
51−Note that you need to compile `ghcide` with the same `ghc` as the project you are working on.
52−
53−1. If the `ghc` you are using matches the version (or better is) from `nixpkgs` it‘s easiest to use the `ghcide` from `nixpkgs`. You can do so via
54− ```
55− nix-env -iA haskellPackages.ghcide
56− ```
57− or e.g. including `pkgs.haskellPackages.ghcide` in your projects `shell.nix`.
58− Depending on your `nixpkgs` channel that might not be the newest `ghcide`, though.
59−
60−2. If your `ghc` does not match nixpkgs you should try the [ghcide-nix repository](https://github.com/cachix/ghcide-nix)
61− which provides a `ghcide` via the `haskell.nix` infrastructure.
34+[See ghcide-nix repository](https://github.com/hercules-ci/ghcide-nix)
6235
6336#### With Cabal or Stack
6437
@@ -91,10 +64,6 @@ The `ghcide` executable mostly relies on [`hie-bios`](https://github.com/mpicker
9164
9265If you can't get `ghcide` working outside the editor, see [this setup troubleshooting guide](docs/Setup.md). Once you have got `ghcide` working outside the editor, the next step is to pick which editor to integrate with.
9366
94−### Optimal project setup
95−
96−`ghcide` has been designed to handle projects with hundreds or thousands of modules. If `ghci` can handle it, then `ghcide` should be able to handle it. The only caveat is that this currently requires GHC >= 8.6, and that the first time a module is loaded in the editor will trigger generation of support files in the background if those do not already exist.
97−
9867### Using with VS Code
9968
10069You can install the VSCode extension from the [VSCode
@@ -234,11 +203,7 @@ Add this to your coc-settings.json (which you can edit with :CocConfig):
234203```
235204
236205Here's a nice article on setting up neovim and coc: [Vim and Haskell in
237−2019](http://marco-lopes.com/articles/Vim-and-Haskell-in-2019/) (this is actually for haskell-ide, not ghcide)
238−
239−Here is a Docker container that pins down the build and configuration for
240−Neovim and ghcide on a minimal Debian 10 base system:
241−[docker-ghcide-neovim](https://github.com/carlohamalainen/docker-ghcide-neovim/).
206+2019](http://marco-lopes.com/articles/Vim-and-Haskell-in-2019/)
242207
243208### SpaceVim
244209
@@ -283,32 +248,10 @@ Add this to your coc-settings.json (which you can edit with :CocConfig):
283248This example above describes a setup in which `ghcide` is installed
284249using `stack install ghcide` within a project.
285250
286−### Using with Kakoune
287−
288−Install [kak-lsp](https://github.com/ul/kak-lsp).
289−
290−Change `kak-lsp.toml` to include this:
291−
292−```toml
293−[language.haskell]
294−filetypes = ["haskell"]
295−roots = ["Setup.hs", "stack.yaml", "*.cabal", "cabal.project", "hie.yaml"]
296−command = "ghcide"
297−args = ["--lsp"]
298−```
299−
300251## Hacking on ghcide
301252
302253To build and work on `ghcide` itself, you can use Stack or cabal, e.g.,
303254running `stack test` will execute the test suite.
304−If you are using Windows, you should disable the `auto.crlf` setting and configure your editor to use LF line endings, directly or making it use the existing `.editor-config`.
305−
306−If you are chasing down test failures, you can use the tasty-rerun feature by running tests as
307−
308− stack --stack-yaml=stack84.yaml test --test-arguments "--rerun"
309−
310−This writes a log file called `.tasty-rerun-log` of the failures, and only runs those.
311−See the [tasty-rerun](https://hackage.haskell.org/package/tasty-rerun-1.1.17/docs/Test-Tasty-Ingredients-Rerun.html) documentation for other options.
312255
313256### Building the extension
314257
@@ -325,10 +268,8 @@ Now opening a `.hs` file should work with `ghcide`.
325268
326269## History and relationship to other Haskell IDE's
327270
328−The teams behind this project and the [`haskell-ide-engine`](https://github.com/haskell/haskell-ide-engine#readme) have agreed to join forces under the [`haskell-language-server` project](https://github.com/haskell/haskell-language-server), see the [original announcement](https://neilmitchell.blogspot.com/2020/01/one-haskell-ide-to-rule-them-all.html). The technical work is ongoing, with the likely model being that this project serves as the core, while plugins and integrations are kept in the [`haskell-language-server` project](https://github.com/haskell/haskell-language-server).
329−
330271The code behind `ghcide` was originally developed by [Digital Asset](https://digitalasset.com/) as part of the [DAML programming language](https://github.com/digital-asset/daml). DAML is a smart contract language targeting distributed-ledger runtimes, based on [GHC](https://www.haskell.org/ghc/) with custom language extensions. The DAML programming language has [an IDE](https://webide.daml.com/), and work was done to separate off a reusable Haskell-only IDE (what is now `ghcide`) which the [DAML IDE then builds upon](https://github.com/digital-asset/daml/tree/master/compiler/damlc). Since that time, there have been various [non-Digital Asset contributors](https://github.com/digital-asset/ghcide/graphs/contributors), in addition to continued investment by Digital Asset. All contributions require a [Contributor License Agreement](https://cla.digitalasset.com/digital-asset/ghcide) that states you license the code under the [Apache License](LICENSE).
331272
332−The Haskell community [has](https://github.com/DanielG/ghc-mod) [various](https://github.com/chrisdone/intero) [IDE](https://github.com/rikvdkleij/intellij-haskell) [choices](http://leksah.org/), but the one that had been gathering momentum is [`haskell-ide-engine`](https://github.com/haskell/haskell-ide-engine#readme). Our project owes a debt of gratitude to the `haskell-ide-engine`. We reuse libraries from their ecosystem, including [`hie-bios`](https://github.com/mpickering/hie-bios#readme) (a likely future environment setup layer in `haskell-ide-engine`), [`haskell-lsp`](https://github.com/alanz/haskell-lsp#readme) and [`lsp-test`](https://github.com/bubba/lsp-test#readme) (the `haskell-ide-engine` [LSP protocol](https://microsoft.github.io/language-server-protocol/) pieces). We make heavy use of their contributions to GHC itself, in particular the work to make GHC take string buffers rather than files.
273+The Haskell community [has](https://github.com/DanielG/ghc-mod) [various](https://github.com/chrisdone/intero) [IDE](https://github.com/rikvdkleij/intellij-haskell) [choices](http://leksah.org/), but the one that has been gathering momentum is [`haskell-ide-engine`](https://github.com/haskell/haskell-ide-engine#readme). Our project owes a debt of gratitude to the `haskell-ide-engine`. We reuse libraries from their ecosystem, including [`hie-bios`](https://github.com/mpickering/hie-bios#readme) (a likely future environment setup layer in `haskell-ide-engine`), [`haskell-lsp`](https://github.com/alanz/haskell-lsp#readme) and [`lsp-test`](https://github.com/bubba/lsp-test#readme) (the `haskell-ide-engine` [LSP protocol](https://microsoft.github.io/language-server-protocol/) pieces). We make heavy use of their contributions to GHC itself, in particular the work to make GHC take string buffers rather than files. While `ghcide` is not a part of `haskell-ide-engine`, we feel it _could_ form the core of a future version - but such decisions are up to the `haskell-ide-engine` contributors.
333274
334275The best summary of the architecture of `ghcide` is available [this talk](https://www.youtube.com/watch?v=cijsaeWNf2E&list=PLxxF72uPfQVRdAsvj7THoys-nVj-oc4Ss) ([slides](https://ndmitchell.com/downloads/slides-making_a_haskell_ide-07_sep_2019.pdf)), given at [MuniHac 2019](https://munihac.de/2019.html). However, since that talk the project has renamed from `hie-core` to `ghcide`, and the repo has moved to [this location](https://github.com/digital-asset/ghcide/).
azure-pipelines.yml+184−3
@@ -4,7 +4,6 @@ trigger:
44 branches:
55 include:
66 - master
7− - azure*
87
98# Enable PR triggers that target the master branch
109pr:
@@ -14,5 +13,187 @@ pr:
1413 - master
1514
1615jobs:
17− - template: ./.azure/linux-stack.yml
18− - template: ./.azure/windows-stack.yml
16+ - job: ghcide_stack_86
17+ timeoutInMinutes: 60
18+ pool:
19+ vmImage: 'ubuntu-latest'
20+ steps:
21+ - checkout: self
22+ - task: CacheBeta@0
23+ inputs:
24+ key: stack-cache-v2 | $(Agent.OS) | $(Build.SourcesDirectory)/stack.yaml | $(Build.SourcesDirectory)/ghcide.cabal
25+ path: .azure-cache
26+ cacheHitVar: CACHE_RESTORED
27+ displayName: "Cache stack artifacts"
28+ - bash: |
29+ mkdir -p ~/.stack
30+ tar xzf .azure-cache/stack-root.tar.gz -C $HOME
31+ displayName: "Unpack cache"
32+ condition: eq(variables.CACHE_RESTORED, 'true')
33+ - bash: |
34+ ./fmt.sh
35+ displayName: "HLint via ./fmt.sh"
36+ - bash: |
37+ sudo apt-get install -y g++ gcc libc6-dev libffi-dev libgmp-dev make zlib1g-dev
38+ if ! which stack >/dev/null 2>&1; then
39+ curl -sSL https://get.haskellstack.org/ | sh
40+ fi
41+ displayName: 'Install Stack'
42+ - bash: stack setup
43+ displayName: 'stack setup'
44+ - bash: stack build --only-dependencies
45+ displayName: 'stack build --only-dependencies'
46+ - bash: stack test --ghc-options=-Werror || stack test --ghc-options=-Werror || stack test --ghc-options=-Werror
47+ # ghcide stack tests are flaky, see https://github.com/digital-asset/daml/issues/2606.
48+ displayName: 'stack test --ghc-options=-Werror'
49+ - bash: |
50+ mkdir -p .azure-cache
51+ tar czf .azure-cache/stack-root.tar.gz -C $HOME .stack
52+ displayName: "Pack cache"
53+ - bash: |
54+ set -euo pipefail
55+ MESSAGE=$(git log --pretty=format:%s -n1)
56+ curl -XPOST \
57+ -i \
58+ -H 'Content-type: application/json' \
59+ --data "{\"text\":\"<!here> *FAILED* $(Agent.JobName): <https://dev.azure.com/digitalasset/ghcide/_build/results?buildId=$(Build.BuildId)|$MESSAGE>\n\"}" \
60+ $(Slack.URL)
61+ condition: and(failed(), eq(variables['Build.SourceBranchName'], 'master'))
62+ - job: ghcide_stack_84
63+ timeoutInMinutes: 60
64+ pool:
65+ vmImage: 'ubuntu-latest'
66+ steps:
67+ - checkout: self
68+ - task: CacheBeta@0
69+ inputs:
70+ key: stack-cache-v2 | $(Agent.OS) | $(Build.SourcesDirectory)/stack84.yaml | $(Build.SourcesDirectory)/ghcide.cabal
71+ path: .azure-cache
72+ cacheHitVar: CACHE_RESTORED
73+ displayName: "Cache stack artifacts"
74+ - bash: |
75+ mkdir -p ~/.stack
76+ tar xzf .azure-cache/stack-root.tar.gz -C $HOME
77+ displayName: "Unpack cache"
78+ condition: eq(variables.CACHE_RESTORED, 'true')
79+ - bash: |
80+ ./fmt.sh
81+ displayName: "HLint via ./fmt.sh"
82+ - bash: |
83+ sudo apt-get install -y g++ gcc libc6-dev libffi-dev libgmp-dev make zlib1g-dev
84+ if ! which stack >/dev/null 2>&1; then
85+ curl -sSL https://get.haskellstack.org/ | sh
86+ fi
87+ displayName: 'Install Stack'
88+ - bash: stack setup --stack-yaml=stack84.yaml
89+ displayName: 'stack setup --stack-yaml=stack84.yaml'
90+ - bash: stack build --only-dependencies --stack-yaml=stack84.yaml
91+ displayName: 'stack build --only-dependencies --stack-yaml=stack84.yaml'
92+ - bash: stack test --stack-yaml=stack84.yaml --ghc-options=-Werror || stack test --stack-yaml=stack84.yaml --ghc-options=-Werror || stack test --stack-yaml=stack84.yaml --ghc-options=-Werror
93+ # ghcide stack tests are flaky, see https://github.com/digital-asset/daml/issues/2606.
94+ displayName: 'stack test --stack-yaml=stack84.yaml --ghc-options=-Werror'
95+ - bash: |
96+ mkdir -p .azure-cache
97+ tar czf .azure-cache/stack-root.tar.gz -C $HOME .stack
98+ displayName: "Pack cache"
99+ - bash: |
100+ set -euo pipefail
101+ MESSAGE=$(git log --pretty=format:%s -n1)
102+ curl -XPOST \
103+ -i \
104+ -H 'Content-type: application/json' \
105+ --data "{\"text\":\"<!here> *FAILED* $(Agent.JobName): <https://dev.azure.com/digitalasset/ghcide/_build/results?buildId=$(Build.BuildId)|$MESSAGE>\n\"}" \
106+ $(Slack.URL)
107+ condition: and(failed(), eq(variables['Build.SourceBranchName'], 'master'))
108+ - job: ghcide_stack_88
109+ timeoutInMinutes: 60
110+ pool:
111+ vmImage: 'ubuntu-latest'
112+ steps:
113+ - checkout: self
114+ - task: CacheBeta@0
115+ inputs:
116+ key: stack-cache-v2 | $(Agent.OS) | $(Build.SourcesDirectory)/stack88.yaml | $(Build.SourcesDirectory)/ghcide.cabal
117+ path: .azure-cache
118+ cacheHitVar: CACHE_RESTORED
119+ displayName: "Cache stack artifacts"
120+ - bash: |
121+ mkdir -p ~/.stack
122+ tar xzf .azure-cache/stack-root.tar.gz -C $HOME
123+ displayName: "Unpack cache"
124+ condition: eq(variables.CACHE_RESTORED, 'true')
125+ - bash: |
126+ ./fmt.sh
127+ displayName: "HLint via ./fmt.sh"
128+ - bash: |
129+ sudo apt-get install -y g++ gcc libc6-dev libffi-dev libgmp-dev make zlib1g-dev
130+ if ! which stack >/dev/null 2>&1; then
131+ curl -sSL https://get.haskellstack.org/ | sh
132+ fi
133+ displayName: 'Install Stack'
134+ - bash: stack setup --stack-yaml=stack88.yaml
135+ displayName: 'stack setup --stack-yaml=stack88.yaml'
136+ - bash: stack build --only-dependencies --stack-yaml=stack88.yaml
137+ displayName: 'stack build --only-dependencies --stack-yaml=stack88.yaml'
138+ - bash: stack test --stack-yaml=stack88.yaml --ghc-options=-Werror || stack test --stack-yaml=stack88.yaml --ghc-options=-Werror || stack test --stack-yaml=stack88.yaml --ghc-options=-Werror
139+ # ghcide stack tests are flaky, see https://github.com/digital-asset/daml/issues/2606.
140+ displayName: 'stack test --stack-yaml=stack88.yaml --ghc-options=-Werror'
141+ - bash: |
142+ mkdir -p .azure-cache
143+ tar czf .azure-cache/stack-root.tar.gz -C $HOME .stack
144+ displayName: "Pack cache"
145+ - bash: |
146+ set -euo pipefail
147+ MESSAGE=$(git log --pretty=format:%s -n1)
148+ curl -XPOST \
149+ -i \
150+ -H 'Content-type: application/json' \
151+ --data "{\"text\":\"<!here> *FAILED* $(Agent.JobName): <https://dev.azure.com/digitalasset/ghcide/_build/results?buildId=$(Build.BuildId)|$MESSAGE>\n\"}" \
152+ $(Slack.URL)
153+ condition: and(failed(), eq(variables['Build.SourceBranchName'], 'master'))
154+ - job: ghcide_stack_ghc_lib_88
155+ timeoutInMinutes: 60
156+ pool:
157+ vmImage: 'ubuntu-latest'
158+ steps:
159+ - checkout: self
160+ - task: CacheBeta@0
161+ inputs:
162+ key: stack-cache-v2 | $(Agent.OS) | $(Build.SourcesDirectory)/stack-ghc-lib.yaml | $(Build.SourcesDirectory)/ghcide.cabal
163+ path: .azure-cache
164+ cacheHitVar: CACHE_RESTORED
165+ displayName: "Cache stack artifacts"
166+ - bash: |
167+ mkdir -p ~/.stack
168+ tar xzf .azure-cache/stack-root.tar.gz -C $HOME
169+ displayName: "Unpack cache"
170+ condition: eq(variables.CACHE_RESTORED, 'true')
171+ - bash: |
172+ ./fmt.sh
173+ displayName: "HLint via ./fmt.sh"
174+ - bash: |
175+ sudo apt-get install -y g++ gcc libc6-dev libffi-dev libgmp-dev make zlib1g-dev
176+ if ! which stack >/dev/null 2>&1; then
177+ curl -sSL https://get.haskellstack.org/ | sh
178+ fi
179+ displayName: 'Install Stack'
180+ - bash: stack setup --stack-yaml=stack-ghc-lib.yaml
181+ displayName: 'stack setup --stack-yaml=stack-ghc-lib.yaml'
182+ - bash: stack build --only-dependencies --stack-yaml=stack-ghc-lib.yaml
183+ displayName: 'stack build --only-dependencies --stack-yaml=stack-ghc-lib.yaml'
184+ - bash: stack test --stack-yaml=stack-ghc-lib.yaml --ghc-options=-Werror || stack test --stack-yaml=stack-ghc-lib.yaml --ghc-options=-Werror || stack test --stack-yaml=stack-ghc-lib.yaml --ghc-options=-Werror
185+ # ghcide stack tests are flaky, see https://github.com/digital-asset/daml/issues/2606.
186+ displayName: 'stack test --stack-yaml=stack-ghc-lib.yaml --ghc-options=-Werror'
187+ - bash: |
188+ mkdir -p .azure-cache
189+ tar czf .azure-cache/stack-root.tar.gz -C $HOME .stack
190+ displayName: "Pack cache"
191+ - bash: |
192+ set -euo pipefail
193+ MESSAGE=$(git log --pretty=format:%s -n1)
194+ curl -XPOST \
195+ -i \
196+ -H 'Content-type: application/json' \
197+ --data "{\"text\":\"<!here> *FAILED* $(Agent.JobName): <https://dev.azure.com/digitalasset/ghcide/_build/results?buildId=$(Build.BuildId)|$MESSAGE>\n\"}" \
198+ $(Slack.URL)
199+ condition: and(failed(), eq(variables['Build.SourceBranchName'], 'master'))
bench/Main.hsdeleted−287
@@ -1,287 +0,0 @@
1−{-# LANGUAGE ConstraintKinds #-}
2−{-# LANGUAGE ExistentialQuantification #-}
3−{-# LANGUAGE ImplicitParams #-}
4−
5−{- An automated benchmark built around the simple experiment described in:
6−
7− > https://neilmitchell.blogspot.com/2020/05/fixing-space-leaks-in-ghcide.html
8−
9− As an example project, it unpacks Cabal-3.2.0.0 in the local filesystem and
10− loads the module 'Distribution.Simple'. The rationale for this choice is:
11−
12− - It's convenient to download with `cabal unpack Cabal-3.2.0.0`
13− - It has very few dependencies, and all are already needed to build ghcide
14− - Distribution.Simple has 235 transitive module dependencies, so non trivial
15−
16− The experiments are sequences of lsp commands scripted using lsp-test.
17− A more refined approach would be to record and replay real IDE interactions,
18− once the replay functionality is available in lsp-test.
19− A more declarative approach would be to reuse ide-debug-driver:
20−
21− > https://github.com/digital-asset/daml/blob/master/compiler/damlc/ide-debug-driver/README.md
22−
23− The result of an experiment is a total duration in seconds after a preset
24− number of iterations. There is ample room for improvement:
25− - Statistical analysis to detect outliers and auto infer the number of iterations needed
26− - GC stats analysis (currently -S is printed as part of the experiment)
27− - Analyisis of performance over the commit history of the project
28−
29− How to run:
30− 1. `cabal bench`
31− 2. `cabal exec <absolute-path-to-ghcide-bench> -- ghcide-bench-options`
32−
33− Note that the package database influences the response times of certain actions,
34− e.g. code actions, and therefore the two methods above do not necessarily
35− produce the same results.
36−
37− -}
38−
39−import Control.Applicative.Combinators
40−import Control.Concurrent
41−import Control.Exception.Safe
42−import Control.Monad.Extra
43−import Control.Monad.IO.Class
44−import Data.Aeson
45−import Data.List
46−import Data.Maybe
47−import Data.Version
48−import Language.Haskell.LSP.Test
49−import Language.Haskell.LSP.Types
50−import Language.Haskell.LSP.Types.Capabilities
51−import Numeric.Natural
52−import Options.Applicative
53−import System.Directory
54−import System.FilePath ((</>))
55−import System.Process
56−import System.Time.Extra
57−
58−-- Points to a string in the target file,
59−-- convenient for hygienic edits
60−hygienicP :: Position
61−hygienicP = Position 854 23
62−
63−-- Points to the middle of an identifier,
64−-- convenient for requesting goto-def, hover and completions
65−identifierP :: Position
66−identifierP = Position 853 12
67−
68−main :: IO ()
69−main = do
70− config <- execParser $ info configP fullDesc
71− let ?config = config
72−
73− output "starting test"
74−
75− cleanUp <- setup
76−
77− runBenchmarks
78− [ ---------------------------------------------------------------------------------------
79− bench "hover" 10 $ \doc ->
80− isJust <$> getHover doc identifierP,
81− ---------------------------------------------------------------------------------------
82− bench "getDefinition" 10 $ \doc ->
83− not . null <$> getDefinitions doc identifierP,
84− ---------------------------------------------------------------------------------------
85− bench "documentSymbols" 100 $
86− fmap (either (not . null) (not . null)) . getDocumentSymbols,
87− ---------------------------------------------------------------------------------------
88− bench "documentSymbols after edit" 100 $ \doc -> do
89− let change =
90− TextDocumentContentChangeEvent
91− { _range = Just (Range hygienicP hygienicP),
92− _rangeLength = Nothing,
93− _text = " "
94− }
95− changeDoc doc [change]
96− either (not . null) (not . null) <$> getDocumentSymbols doc,
97− ---------------------------------------------------------------------------------------
98− bench "completions after edit" 10 $ \doc -> do
99− let change =
100− TextDocumentContentChangeEvent
101− { _range = Just (Range hygienicP hygienicP),
102− _rangeLength = Nothing,
103− _text = " "
104− }
105− changeDoc doc [change]
106− not . null <$> getCompletions doc identifierP,
107− ---------------------------------------------------------------------------------------
108− benchWithSetup
109− "code actions"
110− 10
111− ( \doc -> do
112− let p = identifierP
113− let change =
114− TextDocumentContentChangeEvent
115− { _range = Just (Range p p),
116− _rangeLength = Nothing,
117− _text = "a"
118− }
119− changeDoc doc [change]
120− void (skipManyTill anyMessage message :: Session WorkDoneProgressEndNotification)
121− return p
122− )
123− ( \p doc -> do
124− not . null <$> getCodeActions doc (Range p p)
125− ),
126− ---------------------------------------------------------------------------------------
127− bench "code actions after edit" 10 $ \doc -> do
128− let p = identifierP
129− let change =
130− TextDocumentContentChangeEvent
131− { _range = Just (Range p p),
132− _rangeLength = Nothing,
133− _text = "a"
134− }
135− changeDoc doc [change]
136− void (skipManyTill anyMessage message :: Session WorkDoneProgressEndNotification)
137− not . null <$> getCodeActions doc (Range p p)
138− ]
139− `finally` cleanUp
140−
141−---------------------------------------------------------------------------------------------
142−
143−examplePackageName :: String
144−examplePackageName = "Cabal"
145−
146−examplePackageVersion :: Version
147−examplePackageVersion = makeVersion [3, 2, 0, 0]
148−
149−examplePackage :: String
150−examplePackage = examplePackageName <> "-" <> showVersion examplePackageVersion
151−
152−exampleModulePath :: FilePath
153−exampleModulePath = "Distribution" </> "Simple.hs"
154−
155−examplesPath :: FilePath
156−examplesPath = "bench/example"
157−
158−data Config = Config
159− { verbose :: !Bool,
160− -- For some reason, the Shake profile files are truncated and won't load
161− shakeProfiling :: !(Maybe FilePath),
162− outputCSV :: !Bool
163− }
164−
165−type HasConfig = (?config :: Config)
166−
167−configP :: Parser Config
168−configP = Config
169− <$> (not <$> switch (long "quiet"))
170− <*> optional (strOption (long "shake-profiling" <> metavar "PATH"))
171− <*> switch (long "csv")
172−
173−output :: (MonadIO m, HasConfig) => String -> m ()
174−output = if verbose ?config then liftIO . putStrLn else (\_ -> pure ())
175−
176−---------------------------------------------------------------------------------------
177−
178−type Experiment = TextDocumentIdentifier -> Session Bool
179−
180−data Bench = forall setup.
181− Bench
182− { name :: !String,
183− samples :: !Natural,
184− benchSetup :: TextDocumentIdentifier -> Session setup,
185− experiment :: setup -> Experiment
186− }
187−
188−bench :: String -> Natural -> Experiment -> Bench
189−bench name samples userExperiment = Bench {..}
190− where
191− experiment () = userExperiment
192− benchSetup _ = return ()
193−
194−benchWithSetup ::
195− String ->
196− Natural ->
197− (TextDocumentIdentifier -> Session p) ->
198− (p -> Experiment) ->
199− Bench
200−benchWithSetup = Bench
201−
202−runBenchmarks :: HasConfig => [Bench] -> IO ()
203−runBenchmarks benchmarks = do
204− results <- forM benchmarks $ \b -> (b,) <$> runBench b
205−
206− forM_ results $ \(Bench {name, samples}, duration) ->
207− output $
208− "TOTAL "
209− <> name
210− <> " = "
211− <> showDuration duration
212− <> " ("
213− <> show samples
214− <> " repetitions)"
215−
216− when (outputCSV ?config) $ do
217− putStrLn $ intercalate ", " $ map name benchmarks
218− putStrLn $ intercalate ", " $ map (showDuration . snd) results
219−
220−runBench :: HasConfig => Bench -> IO Seconds
221−runBench Bench {..} = handleAny (\e -> print e >> return (-1))
222− $ runSessionWithConfig conf cmd lspTestCaps dir
223− $ do
224− doc <- openDoc exampleModulePath "haskell"
225− void (skipManyTill anyMessage message :: Session WorkDoneProgressEndNotification)
226−
227− liftIO $ output $ "Running " <> name <> " benchmark"
228− userState <- benchSetup doc
229− let loop 0 = return True
230− loop n = do
231− (t, res) <- duration $ experiment userState doc
232− if not res
233− then return False
234− else do
235− output (showDuration t)
236− loop (n -1)
237−
238− (t, res) <- duration $ loop samples
239−
240− exitServer
241− -- sleeep to give ghcide a chance to print the RTS stats
242− liftIO $ threadDelay 50000
243−
244− return $ if res then t else -1
245− where
246− cmd =
247− unwords $
248− [ "ghcide",
249− "--lsp",
250− "--cwd",
251− dir,
252− "+RTS",
253− "-S",
254− "-RTS"
255− ]
256− ++ concat
257− [ ["--shake-profiling", path]
258− | Just path <- [shakeProfiling ?config]
259− ]
260− dir = "bench/example/" <> examplePackage
261− lspTestCaps =
262− fullCaps {_window = Just $ WindowClientCapabilities $ Just True}
263− conf =
264− defaultConfig
265− { logStdErr = verbose ?config,
266− logMessages = False,
267− logColor = False
268− }
269−
270−setup :: HasConfig => IO (IO ())
271−setup = do
272− alreadyExists <- doesDirectoryExist examplesPath
273− when alreadyExists $ removeDirectoryRecursive examplesPath
274− callCommand $ "cabal get -v0 " <> examplePackage <> " -d " <> examplesPath
275− writeFile
276− (examplesPath </> examplePackage </> "hie.yaml")
277− ("cradle: {cabal: {component: " <> show examplePackageName <> "}}")
278−
279− whenJust (shakeProfiling ?config) $ createDirectoryIfMissing True
280−
281− return $ removeDirectoryRecursive examplesPath
282−
283−-- | Asks the server to shutdown and exit politely
284−exitServer :: Session ()
285−exitServer = request_ Shutdown (Nothing :: Maybe Value) >> sendNotification Exit ExitParams
286−
287−--------------------------------------------------------------------------------------------
docs/Setup.md+1−38
@@ -54,21 +54,8 @@ with the following content:
5454cradle: {cabal: {component: "mylibrary"}}
5555```
5656
57−If you are using stack, find the list of names you can use:
58−
59− $ stack ide targets
60− mypackage:lib
61− mypackage:exe:mypackage-exe
62− mypackage:test:mypackage-test
63−
64−and create a `hie.yaml` file as follows:
65−
66− {stack: {component: "mypackage:lib"}}
67−
6857## ghc: readCreateProcess: does not exist
6958
70−On Linux: try `stack exec ghcide`` instead of `ghcide` directly.
71−
7259I was getting this in Windows: `ghcide.exe: ghc: readCreateProcess: does not exist (No such file or directory)`
7360
7461And we figured a hack around for this:
@@ -82,21 +69,7 @@ Since I use stack. Required if you don't have a `ghc` on your path.
8269
8370## Could not find module ...
8471
85−Try adding an explicit `hie.yaml` file and see if that helps.
86−
87−## Ambiguous main module
88−
89−```console
90−$ stack exec ghcide
91−
92−...
93−
94−ghcide: CradleError (ExitFailure 1) ["Failed to parse result of calling stack","","* * * * * * * *","The main module to load is ambiguous. Candidates are: ","1. Package `mypackage' component mypackage:exe:mypackage-exe with main-is file: /home/user/mypackage/app/Main.hs","2. Package `mypackage' component mypackage:exe:otherbin-exe with main-is file: /home/user/mypackage/app/otherbin.hs","You can specify which one to pick by: "," * Specifying targets to stack ghci e.g. stack ghci mypackage:exe:mypackage-exe"," * Specifying what the main is e.g. stack ghci --main-is mypackage:exe:mypackage-exe"," * Choosing from the candidate above [1..2]","* * * * * * * *","","<stdin>: hGetLine: end of file"]
95−```
96−
97−Add a `hie.yaml` file to specify the module, e.g.
98−
99− cradle: {stack: {component: "mypackage:exe:mypackage-exe"}}
72+Try adding an explicit hie.yaml file and see if that helps.
10073
10174## Works in `ghcide` but not my editor
10275
@@ -133,13 +106,3 @@ find ~/.emacs.d -name '*.elc' -exec rm {} \;
133106## Docker stack builds
134107
135108You're likely to see `ghcide: (ExitFailure 1,"","")`. Because ghcide can't get at the ghc installed inside Docker, your best bet is to `stack exec ghcide` and make sure `ghcide` is installed within the container. Full details at [issue 221](https://github.com/digital-asset/ghcide/issues/221).
136−
137−## stty error on Windows + Stack
138−
139−If you get an error like:
140−
141−```
142−ghcide.exe: CradleError (ExitFailure 1) ["Failed to parse result of calling stack","'stty' is not recognized as an internal or external command,","operable program or batch file."
143−```
144−
145−Then the workaround from https://github.com/haskell/haskell-ide-engine/issues/1428#issuecomment-547530794 might help.
exe/Arguments.hs−4
@@ -12,8 +12,6 @@ data Arguments = Arguments
1212 ,argFiles :: [FilePath]
1313 ,argsVersion :: Bool
1414 ,argsShakeProfiling :: Maybe FilePath
15− ,argsTesting :: Bool
16− ,argsThreads :: Int
1715 }
1816
1917getArguments :: IO Arguments
@@ -31,5 +29,3 @@ arguments = Arguments
3129 <*> many (argument str (metavar "FILES/DIRS..."))
3230 <*> switch (long "version" <> help "Show ghcide and GHC versions")
3331 <*> optional (strOption $ long "shake-profiling" <> metavar "DIR" <> help "Dump profiling reports to this directory")
34− <*> switch (long "test" <> help "Enable additional lsp messages used by the testsuite")
35− <*> option auto (short 'j' <> help "Number of threads (0: automatic)" <> metavar "NUM" <> value 0 <> showDefault)
exe/Main.hs+79−546
@@ -3,31 +3,19 @@
33{-# OPTIONS_GHC -Wno-dodgy-imports #-} -- GHC no longer exports def in GHC 8.6 and above
44{-# LANGUAGE CPP #-} -- To get precise GHC version
55{-# LANGUAGE TemplateHaskell #-}
6−{-# LANGUAGE TypeFamilies #-}
76
87module Main(main) where
98
10−import Linker (initDynLinker)
11−import Data.IORef
12−import NameCache
13−import Packages
14−import Module
159import Arguments
16−import Control.Concurrent.Async
10+import Data.Maybe
11+import Data.List.Extra
12+import System.FilePath
1713import Control.Concurrent.Extra
1814import Control.Exception
1915import Control.Monad.Extra
2016import Control.Monad.IO.Class
2117import Data.Default
22−import Data.Either
23−import Data.Function
24−import Data.List.Extra
25−import Data.Maybe
26−import qualified Data.Text as T
27−import qualified Data.Text.IO as T
28−import Data.Time.Clock (UTCTime)
29−import Data.Version
30−import Development.IDE.Core.Debouncer
18+import System.Time.Extra
3119import Development.IDE.Core.FileStore
3220import Development.IDE.Core.OfInterest
3321import Development.IDE.Core.Service
@@ -40,42 +28,31 @@ import Development.IDE.Types.Diagnostics
4028import Development.IDE.Types.Options
4129import Development.IDE.Types.Logger
4230import Development.IDE.GHC.Util
43−import Development.IDE.Plugin
44−import Development.IDE.Plugin.Completions as Completions
45−import Development.IDE.Plugin.CodeAction as CodeAction
46−import qualified Language.Haskell.LSP.Core as LSP
31+import qualified Data.Text as T
32+import qualified Data.Text.IO as T
4733import Language.Haskell.LSP.Messages
48−import Language.Haskell.LSP.Types
49−import Data.Aeson (ToJSON(toJSON))
34+import Language.Haskell.LSP.Types (LspId(IdInt))
35+import Linker
36+import Data.Version
5037import Development.IDE.LSP.LanguageServer
51−import qualified System.Directory.Extra as IO
38+import System.Directory.Extra as IO
5239import System.Environment
5340import System.IO
5441import System.Exit
55−import System.FilePath
56−import System.Directory
57−import System.Time.Extra
58−import HIE.Bios.Environment (addCmdOpts, makeDynFlagsAbsolute)
5942import Paths_ghcide
6043import Development.GitRev
6144import Development.Shake (Action, action)
62−import qualified Data.HashSet as HashSet
63−import qualified Data.HashMap.Strict as HM
45+import qualified Data.Set as Set
6446import qualified Data.Map.Strict as Map
65−import qualified Crypto.Hash.SHA1 as H
66−import qualified Data.ByteString.Char8 as B
67−import Data.ByteString.Base16 (encode)
68−import DynFlags (gopt_set, gopt_unset, updOptLevel, PackageFlag(..), PackageArg(..))
69−import GhcMonad
70−import HscTypes (HscEnv(..), ic_dflags)
47+
7148import GHC hiding (def)
72−import GHC.Check ( VersionCheck(..), makeGhcVersionChecker )
73−import Data.Either.Extra
49+import qualified GHC.Paths
7450
75−import HIE.Bios.Cradle
76−import HIE.Bios.Types
51+import HIE.Bios
7752
78−import Utils
53+-- Set the GHC libdir to the nix libdir if it's present.
54+getLibdir :: IO FilePath
55+getLibdir = fromMaybe GHC.Paths.libdir <$> lookupEnv "NIX_GHC_LIBDIR"
7956
8057ghcideVersion :: IO String
8158ghcideVersion = do
@@ -102,68 +79,66 @@ main = do
10279 let logger p = Logger $ \pri msg -> when (pri >= p) $ withLock lock $
10380 T.putStrLn $ T.pack ("[" ++ upper (show pri) ++ "] ") <> msg
10481
105− whenJust argsCwd IO.setCurrentDirectory
82+ whenJust argsCwd setCurrentDirectory
10683
107− dir <- IO.getCurrentDirectory
108− command <- makeLspCommandId "typesignature.add"
109−
110− let plugins = Completions.plugin <> CodeAction.plugin
111− onInitialConfiguration = const $ Right ()
112− onConfigurationChange = const $ Right ()
113− options = def { LSP.executeCommandCommands = Just [command]
114− , LSP.completionTriggerCharacters = Just "."
115− }
84+ dir <- getCurrentDirectory
11685
11786 if argLSP then do
11887 t <- offsetTime
11988 hPutStrLn stderr "Starting LSP server..."
120− hPutStrLn stderr "If you are seeing this in a terminal, you probably should have run ghcide WITHOUT the --lsp option!"
121− runLanguageServer options (pluginHandler plugins) onInitialConfiguration onConfigurationChange $ \getLspId event vfs caps -> do
89+ hPutStrLn stderr "If you are seeing this in a terminal, you probably should have run ghcidie WITHOUT the --lsp option!"
90+ runLanguageServer def def $ \getLspId event vfs caps -> do
12291 t <- t
12392 hPutStrLn stderr $ "Started LSP server in " ++ showDuration t
124− let options = (defaultIdeOptions $ loadSessionShake dir)
93+ -- very important we only call loadSession once, and it's fast, so just do it before starting
94+ session <- loadSession dir
95+ let options = (defaultIdeOptions $ return session)
12596 { optReportProgress = clientSupportsProgress caps
12697 , optShakeProfiling = argsShakeProfiling
127− , optTesting = argsTesting
128− , optThreads = argsThreads
12998 }
130− debouncer <- newAsyncDebouncer
131− initialise caps (mainRule >> pluginRules plugins >> action kick)
132− getLspId event (logger minBound) debouncer options vfs
99+ initialise caps (mainRule >> action kick) getLspId event (logger minBound) options vfs
133100 else do
134− -- GHC produces messages with UTF8 in them, so make sure the terminal doesn't error
135− hSetEncoding stdout utf8
136− hSetEncoding stderr utf8
137−
138101 putStrLn $ "Ghcide setup tester in " ++ dir ++ "."
139102 putStrLn "Report bugs at https://github.com/digital-asset/ghcide/issues"
140103
141− putStrLn $ "\nStep 1/4: Finding files to test in " ++ dir
142− files <- expandFiles (argFiles ++ ["." | null argFiles])
143− -- LSP works with absolute file paths, so try and behave similarly
144− files <- nubOrd <$> mapM IO.canonicalizePath files
104+ putStrLn $ "\nStep 1/6: Finding files to test in " ++ dir
105+ files <- nubOrd <$> expandFiles (argFiles ++ ["." | null argFiles])
145106 putStrLn $ "Found " ++ show (length files) ++ " files"
146107
147− putStrLn "\nStep 2/4: Looking for hie.yaml files that control setup"
108+ putStrLn "\nStep 2/6: Looking for hie.yaml files that control setup"
148109 cradles <- mapM findCradle files
149110 let ucradles = nubOrd cradles
150111 let n = length ucradles
151112 putStrLn $ "Found " ++ show n ++ " cradle" ++ ['s' | n /= 1]
152− putStrLn "\nStep 3/4: Initializing the IDE"
113+ sessions <- forM (zipFrom (1 :: Int) ucradles) $ \(i, x) -> do
114+ let msg = maybe ("Implicit cradle for " ++ dir) ("Loading " ++) x
115+ putStrLn $ "\nStep 3/6, Cradle " ++ show i ++ "/" ++ show n ++ ": " ++ msg
116+ cradle <- maybe (loadImplicitCradle $ addTrailingPathSeparator dir) loadCradle x
117+ when (isNothing x) $ print cradle
118+ putStrLn $ "\nStep 4/6, Cradle " ++ show i ++ "/" ++ show n ++ ": Loading GHC Session"
119+ cradleToSession cradle
120+
121+ putStrLn "\nStep 5/6: Initializing the IDE"
153122 vfs <- makeVFSHandle
154− debouncer <- newAsyncDebouncer
155− ide <- initialise def mainRule (pure $ IdInt 0) (showEvent lock) (logger Info) debouncer (defaultIdeOptions $ loadSessionShake dir) vfs
156−
157− putStrLn "\nStep 4/4: Type checking the files"
158− setFilesOfInterest ide $ HashSet.fromList $ map toNormalizedFilePath' files
159− results <- runActionSync ide $ uses TypeCheck (map toNormalizedFilePath' files)
123+ let cradlesToSessions = Map.fromList $ zip ucradles sessions
124+ let filesToCradles = Map.fromList $ zip files cradles
125+ let grab file = fromMaybe (head sessions) $ do
126+ cradle <- Map.lookup file filesToCradles
127+ Map.lookup cradle cradlesToSessions
128+ ide <- initialise def mainRule (pure $ IdInt 0) (showEvent lock) (logger Info) (defaultIdeOptions $ return $ return . grab) vfs
129+
130+ putStrLn "\nStep 6/6: Type checking the files"
131+ setFilesOfInterest ide $ Set.fromList $ map toNormalizedFilePath files
132+ results <- runActionSync ide $ uses TypeCheck $ map toNormalizedFilePath files
160133 let (worked, failed) = partition fst $ zip (map isJust results) files
161134 when (failed /= []) $
162135 putStr $ unlines $ "Files that failed:" : map ((++) " * " . snd) failed
163136
164137 let files xs = let n = length xs in if n == 1 then "1 file" else show n ++ " files"
165138 putStrLn $ "\nCompleted (" ++ files worked ++ " worked, " ++ files failed ++ " failed)"
166− return ()
139+
140+ unless (null failed) exitFailure
141+
167142
168143expandFiles :: [FilePath] -> IO [FilePath]
169144expandFiles = concatMapM $ \x -> do
@@ -172,7 +147,7 @@ expandFiles = concatMapM $ \x -> do
172147 let recurse "." = True
173148 recurse x | "." `isPrefixOf` takeFileName x = False -- skip .git etc
174149 recurse x = takeFileName x `notElem` ["dist","dist-newstyle"] -- cabal directories
175− files <- filter (\x -> takeExtension x `elem` [".hs",".lhs"]) <$> IO.listFilesInside (return . recurse) x
150+ files <- filter (\x -> takeExtension x `elem` [".hs",".lhs"]) <$> listFilesInside (return . recurse) x
176151 when (null files) $
177152 fail $ "Couldn't find any .hs/.lhs files inside directory: " ++ x
178153 return files
@@ -181,414 +156,48 @@ expandFiles = concatMapM $ \x -> do
181156kick :: Action ()
182157kick = do
183158 files <- getFilesOfInterest
184− void $ uses TypeCheck $ HashSet.toList files
159+ void $ uses TypeCheck $ Set.toList files
185160
186161-- | Print an LSP event.
187162showEvent :: Lock -> FromServerMessage -> IO ()
188163showEvent _ (EventFileDiagnostics _ []) = return ()
189−showEvent lock (EventFileDiagnostics (toNormalizedFilePath' -> file) diags) =
164+showEvent lock (EventFileDiagnostics (toNormalizedFilePath -> file) diags) =
190165 withLock lock $ T.putStrLn $ showDiagnosticsColored $ map (file,ShowDiag,) diags
191166showEvent lock e = withLock lock $ print e
192167
193168
194−-- | Run the specific cradle on a specific FilePath via hie-bios.
195−cradleToSessionOpts :: Cradle a -> FilePath -> IO (Either [CradleError] ComponentOptions)
196−cradleToSessionOpts cradle file = do
197− let showLine s = putStrLn ("> " ++ s)
198− cradleRes <- runCradle (cradleOptsProg cradle) showLine file
199− case cradleRes of
200− CradleSuccess r -> pure (Right r)
201− CradleFail err -> return (Left [err])
202− -- For the None cradle perhaps we still want to report an Info
203− -- message about the fact that the file is being ignored.
204− CradleNone -> return (Left [])
205−
206−emptyHscEnv :: IO HscEnv
207−emptyHscEnv = do
169+cradleToSession :: Cradle -> IO HscEnvEq
170+cradleToSession cradle = do
171+ cradleRes <- getCompilerOptions "" cradle
172+ opts <- case cradleRes of
173+ CradleSuccess r -> pure r
174+ CradleFail err -> throwIO err
175+ -- TODO Rather than failing here, we should ignore any files that use this cradle.
176+ -- That will require some more changes.
177+ CradleNone -> fail "'none' cradle is not yet supported"
208178 libdir <- getLibdir
209− env <- runGhc (Just libdir) getSession
179+ env <- runGhc (Just libdir) $ do
180+ _targets <- initSession opts
181+ getSession
210182 initDynLinker env
211− pure env
212−
213−-- | Convert a target to a list of potential absolute paths.
214−-- A TargetModule can be anywhere listed by the supplied include
215−-- directories
216−-- A target file is a relative path but with a specific prefix so just need
217−-- to canonicalise it.
218−targetToFile :: [FilePath] -> TargetId -> IO [NormalizedFilePath]
219−targetToFile is (TargetModule mod) = do
220− let fps = [i </> moduleNameSlashes mod -<.> ext | ext <- exts, i <- is ]
221− exts = ["hs", "hs-boot", "lhs"]
222− mapM (fmap toNormalizedFilePath' . canonicalizePath) fps
223−targetToFile _ (TargetFile f _) = do
224− f' <- canonicalizePath f
225− return [toNormalizedFilePath' f']
226−
227−setNameCache :: IORef NameCache -> HscEnv -> HscEnv
228−setNameCache nc hsc = hsc { hsc_NC = nc }
229−
230−loadSessionShake :: FilePath -> Action (FilePath -> Action (IdeResult HscEnvEq))
231−loadSessionShake fp = do
232− se <- getShakeExtras
233− IdeOptions{optTesting} <- getIdeOptions
234− res <- liftIO $ loadSession optTesting se fp
235− return (fmap liftIO res)
236−
237−-- | This is the key function which implements multi-component support. All
238−-- components mapping to the same hie.yaml file are mapped to the same
239−-- HscEnv which is updated as new components are discovered.
240−loadSession :: Bool -> ShakeExtras -> FilePath -> IO (FilePath -> IO (IdeResult HscEnvEq))
241−loadSession optTesting ShakeExtras{logger, eventer} dir = do
242− -- Mapping from hie.yaml file to HscEnv, one per hie.yaml file
243− hscEnvs <- newVar Map.empty :: IO (Var HieMap)
244− -- Mapping from a Filepath to HscEnv
245− fileToFlags <- newVar Map.empty :: IO (Var FlagsMap)
246−
247− -- This caches the mapping from Mod.hs -> hie.yaml
248− cradleLoc <- memoIO $ \v -> do
249− res <- findCradle v
250− -- Sometimes we get C:, sometimes we get c:, and sometimes we get a relative path
251− -- try and normalise that
252− -- e.g. see https://github.com/digital-asset/ghcide/issues/126
253− res' <- traverse IO.makeAbsolute res
254− return $ normalise <$> res'
255−
256− -- Create a new HscEnv from a hieYaml root and a set of options
257− -- If the hieYaml file already has an HscEnv, the new component is
258− -- combined with the components in the old HscEnv into a new HscEnv
259− -- which contains the union.
260− let packageSetup :: (Maybe FilePath, NormalizedFilePath, ComponentOptions)
261− -> IO (HscEnv, ComponentInfo, [ComponentInfo])
262− packageSetup (hieYaml, cfp, opts) = do
263− -- Parse DynFlags for the newly discovered component
264− hscEnv <- emptyHscEnv
265− (df, targets) <- evalGhcEnv hscEnv $
266− setOptions opts (hsc_dflags hscEnv)
267− dep_info <- getDependencyInfo (componentDependencies opts ++ maybeToList hieYaml)
268− -- Now lookup to see whether we are combining with an existing HscEnv
269− -- or making a new one. The lookup returns the HscEnv and a list of
270− -- information about other components loaded into the HscEnv
271− -- (unitId, DynFlag, Targets)
272− modifyVar hscEnvs $ \m -> do
273− -- Just deps if there's already an HscEnv
274− -- Nothing is it's the first time we are making an HscEnv
275− let oldDeps = Map.lookup hieYaml m
276− let -- Add the raw information about this component to the list
277− -- We will modify the unitId and DynFlags used for
278− -- compilation but these are the true source of
279− -- information.
280− new_deps = RawComponentInfo (thisInstalledUnitId df) df targets cfp opts dep_info
281− : maybe [] snd oldDeps
282− -- Get all the unit-ids for things in this component
283− inplace = map rawComponentUnitId new_deps
284−
285− new_deps' <- forM new_deps $ \RawComponentInfo{..} -> do
286− -- Remove all inplace dependencies from package flags for
287− -- components in this HscEnv
288− let (df2, uids) = removeInplacePackages inplace rawComponentDynFlags
289− let prefix = show rawComponentUnitId
290− -- See Note [Avoiding bad interface files]
291− processed_df <- setCacheDir logger prefix (sort $ map show uids) opts df2
292− -- The final component information, mostly the same but the DynFlags don't
293− -- contain any packages which are also loaded
294− -- into the same component.
295− pure $ ComponentInfo rawComponentUnitId
296− processed_df
297− uids
298− rawComponentTargets
299− rawComponentFP
300− rawComponentCOptions
301− rawComponentDependencyInfo
302− -- Make a new HscEnv, we have to recompile everything from
303− -- scratch again (for now)
304− -- It's important to keep the same NameCache though for reasons
305− -- that I do not fully understand
306− logInfo logger (T.pack ("Making new HscEnv" ++ show inplace))
307− hscEnv <- case oldDeps of
308− Nothing -> emptyHscEnv
309− Just (old_hsc, _) -> setNameCache (hsc_NC old_hsc) <$> emptyHscEnv
310− newHscEnv <-
311− -- Add the options for the current component to the HscEnv
312− evalGhcEnv hscEnv $ do
313− _ <- setSessionDynFlags df
314− getSession
315− -- Modify the map so the hieYaml now maps to the newly created
316− -- HscEnv
317− -- Returns
318− -- . the new HscEnv so it can be used to modify the
319− -- FilePath -> HscEnv map (fileToFlags)
320− -- . The information for the new component which caused this cache miss
321− -- . The modified information (without -inplace flags) for
322− -- existing packages
323− pure (Map.insert hieYaml (newHscEnv, new_deps) m, (newHscEnv, head new_deps', tail new_deps'))
324−
325− let session :: (Maybe FilePath, NormalizedFilePath, ComponentOptions) -> IO (IdeResult HscEnvEq)
326− session (hieYaml, cfp, opts) = do
327− (hscEnv, new, old_deps) <- packageSetup (hieYaml, cfp, opts)
328− -- Make a map from unit-id to DynFlags, this is used when trying to
329− -- resolve imports. (especially PackageImports)
330− let uids = map (\ci -> (componentUnitId ci, componentDynFlags ci)) (new : old_deps)
331−
332− -- For each component, now make a new HscEnvEq which contains the
333− -- HscEnv for the hie.yaml file but the DynFlags for that component
334−
335− -- New HscEnv for the component in question, returns the new HscEnvEq and
336− -- a mapping from FilePath to the newly created HscEnvEq.
337− let new_cache = newComponentCache logger hscEnv uids
338− (cs, res) <- new_cache new
339− -- Modified cache targets for everything else in the hie.yaml file
340− -- which now uses the same EPS and so on
341− cached_targets <- concatMapM (fmap fst . new_cache) old_deps
342− modifyVar_ fileToFlags $ \var -> do
343− pure $ Map.insert hieYaml (HM.fromList (cs ++ cached_targets)) var
344−
345− return (fst res)
346−
347− let consultCradle :: Maybe FilePath -> FilePath -> IO (IdeResult HscEnvEq)
348− consultCradle hieYaml cfp = do
349− when optTesting $ eventer $ notifyCradleLoaded cfp
350− logInfo logger $ T.pack ("Consulting the cradle for " <> show cfp)
351− cradle <- maybe (loadImplicitCradle $ addTrailingPathSeparator dir) loadCradle hieYaml
352− eopts <- cradleToSessionOpts cradle cfp
353− logDebug logger $ T.pack ("Session loading result: " <> show eopts)
354− case eopts of
355− -- The cradle gave us some options so get to work turning them
356− -- into and HscEnv.
357− Right opts -> do
358− session (hieYaml, toNormalizedFilePath' cfp, opts)
359− -- Failure case, either a cradle error or the none cradle
360− Left err -> do
361− dep_info <- getDependencyInfo (maybeToList hieYaml)
362− let ncfp = toNormalizedFilePath' cfp
363− let res = (map (renderCradleError ncfp) err, Nothing)
364− modifyVar_ fileToFlags $ \var -> do
365− pure $ Map.insertWith HM.union hieYaml (HM.singleton ncfp (res, dep_info)) var
366− return res
367−
368− -- This caches the mapping from hie.yaml + Mod.hs -> [String]
369− let sessionOpts :: (Maybe FilePath, FilePath) -> IO (IdeResult HscEnvEq)
370− sessionOpts (hieYaml, file) = do
371− v <- fromMaybe HM.empty . Map.lookup hieYaml <$> readVar fileToFlags
372− cfp <- canonicalizePath file
373− case HM.lookup (toNormalizedFilePath' cfp) v of
374− Just (opts, old_di) -> do
375− deps_ok <- checkDependencyInfo old_di
376− if not deps_ok
377− then do
378− -- If the dependencies are out of date then clear both caches and start
379− -- again.
380− modifyVar_ fileToFlags (const (return Map.empty))
381− -- Keep the same name cache
382− modifyVar_ hscEnvs (return . Map.adjust (\(h, _) -> (h, [])) hieYaml )
383− consultCradle hieYaml cfp
384− else return opts
385− Nothing -> consultCradle hieYaml cfp
183+ newHscEnvEq env
386184
387− dummyAs <- async $ return (error "Uninitialised")
388− runningCradle <- newVar dummyAs :: IO (Var (Async (IdeResult HscEnvEq)))
389− -- The main function which gets options for a file. We only want one of these running
390− -- at a time. Therefore the IORef contains the currently running cradle, if we try
391− -- to get some more options then we wait for the currently running action to finish
392− -- before attempting to do so.
393− let getOptions :: FilePath -> IO (IdeResult HscEnvEq)
394− getOptions file = do
395− hieYaml <- cradleLoc file
396− sessionOpts (hieYaml, file)
397− return $ \file -> do
398− join $ mask_ $ modifyVar runningCradle $ \as -> do
399− -- If the cradle is not finished, then wait for it to finish.
400− void $ wait as
401− as <- async $ getOptions file
402− return (as, wait as)
403185
186+loadSession :: FilePath -> IO (FilePath -> Action HscEnvEq)
187+loadSession dir = do
188+ cradleLoc <- memoIO $ \v -> do
189+ res <- findCradle v
190+ -- Sometimes we get C:, sometimes we get c:, and sometimes we get a relative path
191+ -- try and normalise that
192+ -- e.g. see https://github.com/digital-asset/ghcide/issues/126
193+ res' <- traverse makeAbsolute res
194+ return $ normalise <$> res'
195+ session <- memoIO $ \file -> do
196+ c <- maybe (loadImplicitCradle $ addTrailingPathSeparator dir) loadCradle file
197+ cradleToSession c
198+ return $ \file -> liftIO $ session =<< cradleLoc file
404199
405200
406−-- | Create a mapping from FilePaths to HscEnvEqs
407−newComponentCache
408− :: Logger
409− -> HscEnv
410− -> [(InstalledUnitId, DynFlags)]
411− -> ComponentInfo
412− -> IO ([(NormalizedFilePath, (IdeResult HscEnvEq, DependencyInfo))], (IdeResult HscEnvEq, DependencyInfo))
413−newComponentCache logger hsc_env uids ci = do
414− let df = componentDynFlags ci
415− let hscEnv' = hsc_env { hsc_dflags = df
416− , hsc_IC = (hsc_IC hsc_env) { ic_dflags = df } }
417−
418− versionMismatch <- checkGhcVersion
419− henv <- case versionMismatch of
420− Just mismatch -> return mismatch
421− Nothing -> newHscEnvEq hscEnv' uids
422− let res = (([], Just henv), componentDependencyInfo ci)
423− logDebug logger ("New Component Cache HscEnvEq: " <> T.pack (show res))
424−
425− let is = importPaths df
426− ctargets <- concatMapM (targetToFile is . targetId) (componentTargets ci)
427− -- A special target for the file which caused this wonderful
428− -- component to be created. In case the cradle doesn't list all the targets for
429− -- the component, in which case things will be horribly broken anyway.
430− -- Otherwise, we will immediately attempt to reload this module which
431− -- causes an infinite loop and high CPU usage.
432− let special_target = (componentFP ci, res)
433− let xs = map (,res) ctargets
434− return (special_target:xs, res)
435−
436−{- Note [Avoiding bad interface files]
437−~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
438−Originally, we set the cache directory for the various components once
439−on the first occurrence of the component.
440−This works fine if these components have no references to each other,
441−but you have components that depend on each other, the interface files are
442−updated for each component.
443−After restarting the session and only opening the component that depended
444−on the other, suddenly the interface files of this component are stale.
445−However, from the point of view of `ghcide`, they do not look stale,
446−thus, not regenerated and the IDE shows weird errors such as:
447−```
448−typecheckIface
449−Declaration for Rep_ClientRunFlags
450−Axiom branches Rep_ClientRunFlags:
451− Failed to load interface for ‘Distribution.Simple.Flag’
452− Use -v to see a list of the files searched for.
453−```
454−and
455−```
456−expectJust checkFamInstConsistency
457−CallStack (from HasCallStack):
458− error, called at compiler\\utils\\Maybes.hs:55:27 in ghc:Maybes
459− expectJust, called at compiler\\typecheck\\FamInst.hs:461:30 in ghc:FamInst
460−```
461−
462−To mitigate this, we set the cache directory for each component dependent
463−on the components of the current `HscEnv`, additionally to the component options
464−of the respective components.
465−Assume two components, c1, c2, where c2 depends on c1, and the options of the
466−respective components are co1, co2.
467−If we want to load component c2, followed by c1, we set the cache directory for
468−each component in this way:
469−
470− * Load component c2
471− * (Cache Directory State)
472− - name of c2 + co2
473− * Load component c1
474− * (Cache Directory State)
475− - name of c2 + name of c1 + co2
476− - name of c2 + name of c1 + co1
477−
478−Overall, we created three cache directories. If we opened c1 first, then we
479−create a fourth cache directory.
480−This makes sure that interface files are always correctly updated.
481−
482−Since this causes a lot of recompilation, we only update the cache-directory,
483−if the dependencies of a component have really changed.
484−E.g. when you load two executables, they can not depend on each other. They
485−should be filtered out, such that we dont have to re-compile everything.
486−-}
487−
488−-- | Set the cache-directory based on the ComponentOptions and a list of
489−-- internal packages.
490−-- For the exact reason, see Note [Avoiding bad interface files].
491−setCacheDir :: MonadIO m => Logger -> String -> [String] -> ComponentOptions -> DynFlags -> m DynFlags
492−setCacheDir logger prefix hscComponents comps dflags = do
493− cacheDir <- liftIO $ getCacheDir prefix (hscComponents ++ componentOptions comps)
494− liftIO $ logInfo logger $ "Using interface files cache dir: " <> T.pack cacheDir
495− pure $ dflags
496− & setHiDir cacheDir
497− & setDefaultHieDir cacheDir
498−
499−
500−renderCradleError :: NormalizedFilePath -> CradleError -> FileDiagnostic
501−renderCradleError nfp (CradleError _ec t) =
502− ideErrorText nfp (T.unlines (map T.pack t))
503−
504−-- See Note [Multi Cradle Dependency Info]
505−type DependencyInfo = Map.Map FilePath (Maybe UTCTime)
506−type HieMap = Map.Map (Maybe FilePath) (HscEnv, [RawComponentInfo])
507−type FlagsMap = Map.Map (Maybe FilePath) (HM.HashMap NormalizedFilePath (IdeResult HscEnvEq, DependencyInfo))
508−
509−-- This is pristine information about a component
510−data RawComponentInfo = RawComponentInfo
511− { rawComponentUnitId :: InstalledUnitId
512− -- | Unprocessed DynFlags. Contains inplace packages such as libraries.
513− -- We do not want to use them unprocessed.
514− , rawComponentDynFlags :: DynFlags
515− -- | All targets of this components.
516− , rawComponentTargets :: [Target]
517− -- | Filepath which caused the creation of this component
518− , rawComponentFP :: NormalizedFilePath
519− -- | Component Options used to load the component.
520− , rawComponentCOptions :: ComponentOptions
521− -- | Maps cradle dependencies, such as `stack.yaml`, or `.cabal` file
522− -- to last modification time. See Note [Multi Cradle Dependency Info].
523− , rawComponentDependencyInfo :: DependencyInfo
524− }
525−
526−-- This is processed information about the component, in particular the dynflags will be modified.
527−data ComponentInfo = ComponentInfo
528− { componentUnitId :: InstalledUnitId
529− -- | Processed DynFlags. Does not contain inplace packages such as local
530− -- libraries. Can be used to actually load this Component.
531− , componentDynFlags :: DynFlags
532− -- | Internal units, such as local libraries, that this component
533− -- is loaded with. These have been extracted from the original
534− -- ComponentOptions.
535− , componentInternalUnits :: [InstalledUnitId]
536− -- | All targets of this components.
537− , componentTargets :: [Target]
538− -- | Filepath which caused the creation of this component
539− , componentFP :: NormalizedFilePath
540− -- | Component Options used to load the component.
541− , componentCOptions :: ComponentOptions
542− -- | Maps cradle dependencies, such as `stack.yaml`, or `.cabal` file
543− -- to last modification time. See Note [Multi Cradle Dependency Info]
544− , componentDependencyInfo :: DependencyInfo
545− }
546−
547−-- | Check if any dependency has been modified lately.
548−checkDependencyInfo :: DependencyInfo -> IO Bool
549−checkDependencyInfo old_di = do
550− di <- getDependencyInfo (Map.keys old_di)
551− return (di == old_di)
552−
553−-- Note [Multi Cradle Dependency Info]
554−-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
555−-- Why do we implement our own file modification tracking here?
556−-- The primary reason is that the custom caching logic is quite complicated and going into shake
557−-- adds even more complexity and more indirection. I did try for about 5 hours to work out how to
558−-- use shake rules rather than IO but eventually gave up.
559−
560−-- | Computes a mapping from a filepath to its latest modification date.
561−-- See Note [Multi Cradle Dependency Info] why we do this ourselves instead
562−-- of letting shake take care of it.
563−getDependencyInfo :: [FilePath] -> IO DependencyInfo
564−getDependencyInfo fs = Map.fromList <$> mapM do_one fs
565−
566− where
567− tryIO :: IO a -> IO (Either IOException a)
568− tryIO = try
569−
570− do_one :: FilePath -> IO (FilePath, Maybe UTCTime)
571− do_one fp = (fp,) . eitherToMaybe <$> tryIO (getModificationTime fp)
572−
573−-- | This function removes all the -package flags which refer to packages we
574−-- are going to deal with ourselves. For example, if a executable depends
575−-- on a library component, then this function will remove the library flag
576−-- from the package flags for the executable
577−--
578−-- There are several places in GHC (for example the call to hptInstances in
579−-- tcRnImports) which assume that all modules in the HPT have the same unit
580−-- ID. Therefore we create a fake one and give them all the same unit id.
581−removeInplacePackages :: [InstalledUnitId] -> DynFlags -> (DynFlags, [InstalledUnitId])
582−removeInplacePackages us df = (df { packageFlags = ps
583− , thisInstalledUnitId = fake_uid }, uids)
584− where
585− (uids, ps) = partitionEithers (map go (packageFlags df))
586− fake_uid = toInstalledUnitId (stringToUnitId "fake_uid")
587− go p@(ExposePackage _ (UnitIdArg u) _) = if toInstalledUnitId u `elem` us
588− then Left (toInstalledUnitId u)
589− else Right p
590− go p = Right p
591−
592201-- | Memoize an IO function, with the characteristics:
593202--
594203-- * If multiple people ask for a result simultaneously, make sure you only compute it once.
@@ -605,79 +214,3 @@ memoIO op = do
605214 res <- onceFork $ op k
606215 return (Map.insert k res mp, res)
607216 Just res -> return (mp, res)
608−
609−setOptions :: GhcMonad m => ComponentOptions -> DynFlags -> m (DynFlags, [Target])
610−setOptions (ComponentOptions theOpts compRoot _) dflags = do
611− (dflags', targets) <- addCmdOpts theOpts dflags
612− let dflags'' =
613− -- disabled, generated directly by ghcide instead
614− flip gopt_unset Opt_WriteInterface $
615− -- disabled, generated directly by ghcide instead
616− -- also, it can confuse the interface stale check
617− dontWriteHieFiles $
618− setIgnoreInterfacePragmas $
619− setLinkerOptions $
620− disableOptimisation $
621− makeDynFlagsAbsolute compRoot dflags'
622− -- initPackages parses the -package flags and
623− -- sets up the visibility for each component.
624− (final_df, _) <- liftIO $ initPackages dflags''
625− return (final_df, targets)
626−
627−
628−-- we don't want to generate object code so we compile to bytecode
629−-- (HscInterpreted) which implies LinkInMemory
630−-- HscInterpreted
631−setLinkerOptions :: DynFlags -> DynFlags
632−setLinkerOptions df = df {
633− ghcLink = LinkInMemory
634− , hscTarget = HscNothing
635− , ghcMode = CompManager
636− }
637−
638−setIgnoreInterfacePragmas :: DynFlags -> DynFlags
639−setIgnoreInterfacePragmas df =
640− gopt_set (gopt_set df Opt_IgnoreInterfacePragmas) Opt_IgnoreOptimChanges
641−
642−disableOptimisation :: DynFlags -> DynFlags
643−disableOptimisation df = updOptLevel 0 df
644−
645−setHiDir :: FilePath -> DynFlags -> DynFlags
646−setHiDir f d =
647− -- override user settings to avoid conflicts leading to recompilation
648− d { hiDir = Just f}
649−
650−getCacheDir :: String -> [String] -> IO FilePath
651−getCacheDir prefix opts = IO.getXdgDirectory IO.XdgCache (cacheDir </> prefix ++ "-" ++ opts_hash)
652− where
653− -- Create a unique folder per set of different GHC options, assuming that each different set of
654− -- GHC options will create incompatible interface files.
655− opts_hash = B.unpack $ encode $ H.finalize $ H.updates H.init (map B.pack opts)
656−
657−-- | Sub directory for the cache path
658−cacheDir :: String
659−cacheDir = "ghcide"
660−
661−notifyCradleLoaded :: FilePath -> FromServerMessage
662−notifyCradleLoaded fp =
663− NotCustomServer $
664− NotificationMessage "2.0" (CustomServerMethod cradleLoadedMethod) $
665− toJSON fp
666−
667−cradleLoadedMethod :: T.Text
668−cradleLoadedMethod = "ghcide/cradle/loaded"
669−
670−ghcVersionChecker :: IO VersionCheck
671−ghcVersionChecker = $$(makeGhcVersionChecker (pure <$> getLibdir))
672−
673−checkGhcVersion :: IO (Maybe HscEnvEq)
674−checkGhcVersion = do
675− res <- ghcVersionChecker
676− case res of
677− Failure err -> do
678− putStrLn $ "Error while checking GHC version: " ++ show err
679− return Nothing
680− Mismatch {..} ->
681− return $ Just GhcVersionMismatch {..}
682− _ ->
683− return Nothing
exe/Utils.hsdeleted−9
@@ -1,9 +0,0 @@
1−module Utils (getLibdir) where
2−
3−import qualified GHC.Paths
4−import System.Environment
5−import Data.Maybe
6−
7−-- Set the GHC libdir to the nix libdir if it's present.
8−getLibdir :: IO FilePath
9−getLibdir = fromMaybe GHC.Paths.libdir <$> lookupEnv "NIX_GHC_LIBDIR"
fmt.sh+1−1
@@ -1,3 +1,3 @@
11#!/usr/bin/env bash
22set -eou pipefail
3−curl -sSL https://raw.github.com/ndmitchell/hlint/master/misc/run.sh | sh -s . --with-group=extra
3+curl -sSL https://raw.github.com/ndmitchell/hlint/master/misc/run.sh | sh -s .
ghcide.cabal+23−120
@@ -2,12 +2,12 @@ cabal-version: 1.20
22build-type: Simple
33category: Development
44name: ghcide
5−version: 0.2.0
5+version: 0.0.6
66license: Apache-2.0
77license-file: LICENSE
88author: Digital Asset
99maintainer: Digital Asset
10−copyright: Digital Asset 2018-2020
10+copyright: Digital Asset 2018-2019
1111synopsis: The core of an IDE
1212description:
1313 A library for building Haskell IDE's on top of the GHC API.
@@ -15,13 +15,7 @@ homepage: https://github.com/digital-asset/ghcide#readme
1515bug-reports: https://github.com/digital-asset/ghcide/issues
1616tested-with: GHC==8.6.5
1717extra-source-files: include/ghc-api-version.h README.md CHANGELOG.md
18− test/data/hover/*.hs
19− test/data/multi/cabal.project
20− test/data/multi/hie.yaml
21− test/data/multi/a/a.cabal
22− test/data/multi/a/*.hs
23− test/data/multi/b/b.cabal
24− test/data/multi/b/*.hs
18+ test/data/GotoHover.hs
2519
2620source-repository head
2721 type: git
@@ -36,7 +30,6 @@ library
3630 default-language: Haskell2010
3731 build-depends:
3832 aeson,
39− array,
4033 async,
4134 base == 4.*,
4235 binary,
@@ -48,10 +41,10 @@ library
4841 extra,
4942 fuzzy,
5043 filepath,
51− haddock-library >= 1.8,
44+ haddock-library,
5245 hashable,
53− haskell-lsp-types == 0.22.*,
54− haskell-lsp == 0.22.*,
46+ haskell-lsp-types == 0.19.*,
47+ haskell-lsp == 0.19.*,
5548 mtl,
5649 network-uri,
5750 prettyprinter-ansi-terminal,
@@ -60,14 +53,14 @@ library
6053 regex-tdfa >= 1.3.1.0,
6154 rope-utf16-splay,
6255 safe-exceptions,
63− shake >= 0.18.4,
56+ shake >= 0.17.5,
6457 sorted-list,
6558 stm,
6659 syb,
6760 text,
6861 time,
6962 transformers,
70− unordered-containers >= 0.2.10.0,
63+ unordered-containers,
7164 utf8-string,
7265 hslogger
7366 if flag(ghc-lib)
@@ -80,10 +73,7 @@ library
8073 ghc-boot-th,
8174 ghc-boot,
8275 ghc >= 8.4
83− if os(windows)
84− build-depends:
85− Win32
86− else
76+ if !os(windows)
8777 build-depends:
8878 unix
8979 c-sources:
@@ -109,66 +99,51 @@ library
10999 include-dirs:
110100 include
111101 exposed-modules:
112− Development.IDE.Compat
113− Development.IDE.Core.Debouncer
102+ Development.IDE.Core.Completions
103+ Development.IDE.Core.CompletionsTypes
114104 Development.IDE.Core.FileStore
115− Development.IDE.Core.IdeConfiguration
116105 Development.IDE.Core.OfInterest
117106 Development.IDE.Core.PositionMapping
118107 Development.IDE.Core.Rules
119108 Development.IDE.Core.RuleTypes
120109 Development.IDE.Core.Service
121110 Development.IDE.Core.Shake
122− Development.IDE.GHC.Error
123111 Development.IDE.GHC.Util
124112 Development.IDE.Import.DependencyInformation
125− Development.IDE.LSP.HoverDefinition
126113 Development.IDE.LSP.LanguageServer
127− Development.IDE.LSP.Outline
128114 Development.IDE.LSP.Protocol
129115 Development.IDE.LSP.Server
130− Development.IDE.Spans.Common
131116 Development.IDE.Types.Diagnostics
132117 Development.IDE.Types.Location
133118 Development.IDE.Types.Logger
134119 Development.IDE.Types.Options
135− Development.IDE.Plugin
136− Development.IDE.Plugin.Completions
137− Development.IDE.Plugin.CodeAction
138120 other-modules:
121+ Development.IDE.Core.Debouncer
139122 Development.IDE.Core.Compile
140123 Development.IDE.Core.Preprocessor
141124 Development.IDE.Core.FileExists
142125 Development.IDE.GHC.Compat
143126 Development.IDE.GHC.CPP
127+ Development.IDE.GHC.Error
144128 Development.IDE.GHC.Orphans
145129 Development.IDE.GHC.Warnings
146− Development.IDE.GHC.WithDynFlags
147130 Development.IDE.Import.FindImports
131+ Development.IDE.LSP.CodeAction
132+ Development.IDE.LSP.Completions
133+ Development.IDE.LSP.HoverDefinition
148134 Development.IDE.LSP.Notifications
135+ Development.IDE.LSP.Outline
149136 Development.IDE.Spans.AtPoint
150137 Development.IDE.Spans.Calculate
138+ Development.IDE.Spans.Common
151139 Development.IDE.Spans.Documentation
152140 Development.IDE.Spans.Type
153− Development.IDE.Plugin.CodeAction.PositionIndexed
154− Development.IDE.Plugin.CodeAction.Rules
155− Development.IDE.Plugin.CodeAction.RuleTypes
156− Development.IDE.Plugin.Completions.Logic
157− Development.IDE.Plugin.Completions.Types
158− if (impl(ghc > 8.7) && impl(ghc < 8.10)) || flag(ghc-lib)
159− hs-source-dirs: src-ghc88
160− other-modules:
161− Development.IDE.GHC.HieAst
162− if (impl(ghc > 8.9))
163− hs-source-dirs: src-ghc810
164− other-modules:
165− Development.IDE.GHC.HieAst
166141 ghc-options: -Wall -Wno-name-shadowing
167142
168143executable ghcide-test-preprocessor
169144 default-language: Haskell2010
170145 hs-source-dirs: test/preprocessor
171− ghc-options: -Wall -Wno-name-shadowing
146+ ghc-options: -Wall
172147 main-is: Main.hs
173148 build-depends:
174149 base == 4.*
@@ -190,52 +165,29 @@ executable ghcide
190165 "-with-rtsopts=-I0 -qg -A128M"
191166 main-is: Main.hs
192167 build-depends:
193− time,
194− async,
195168 hslogger,
196− aeson,
197169 base == 4.*,
198− binary,
199− base16-bytestring >=0.1.1 && <0.2,
200− bytestring,
201170 containers,
202− cryptohash-sha1 >=0.11.100 && <0.12,
203171 data-default,
204− deepseq,
205172 directory,
206173 extra,
207174 filepath,
208− ghc-check >= 0.3.0.1 && < 0.4,
209175 ghc-paths,
210176 ghc,
211177 gitrev,
212− hashable,
213178 haskell-lsp,
214− haskell-lsp-types,
215− hie-bios >= 0.5.0 && < 0.6,
179+ hie-bios >= 0.3.2 && < 0.4,
216180 ghcide,
217181 optparse-applicative,
218182 shake,
219− text,
220− unordered-containers
183+ text
221184 other-modules:
222− Utils
223185 Arguments
224186 Paths_ghcide
225187
226188 default-extensions:
227− BangPatterns
228− DeriveFunctor
229− DeriveGeneric
230− GeneralizedNewtypeDeriving
231− LambdaCase
232− NamedFieldPuns
233− OverloadedStrings
234189 RecordWildCards
235− ScopedTypeVariables
236− StandaloneDeriving
237190 TupleSections
238− TypeApplications
239191 ViewPatterns
240192
241193test-suite ghcide-tests
@@ -264,22 +216,13 @@ test-suite ghcide-tests
264216 --------------------------------------------------------------
265217 ghcide,
266218 ghc-typelits-knownnat,
267− haddock-library,
268− haskell-lsp,
269219 haskell-lsp-types,
270− network-uri,
271220 lens,
272− lsp-test >= 0.11.0.1 && < 0.12,
221+ lsp-test >= 0.8,
273222 parser-combinators,
274− QuickCheck,
275− quickcheck-instances,
276− rope-utf16-splay,
277− shake,
278223 tasty,
279− tasty-expected-failure,
280224 tasty-hunit,
281− tasty-quickcheck,
282− tasty-rerun,
225+ tasty-expected-failure,
283226 text
284227 hs-source-dirs: test/cabal test/exe test/src
285228 include-dirs: include
@@ -302,43 +245,3 @@ test-suite ghcide-tests
302245 TupleSections
303246 TypeApplications
304247 ViewPatterns
305−
306−benchmark ghcide-bench
307− type: exitcode-stdio-1.0
308− default-language: Haskell2010
309− build-tool-depends:
310− ghcide:ghcide,
311− ghcide:ghcide-test-preprocessor
312− build-depends:
313− aeson,
314− base,
315− bytestring,
316− containers,
317− directory,
318− extra,
319− filepath,
320− ghcide,
321− lsp-test < 0.12,
322− optparse-applicative,
323− parser-combinators,
324− process,
325− safe-exceptions
326− hs-source-dirs: bench
327− include-dirs: include
328− ghc-options: -threaded -Wall -Wno-name-shadowing
329− main-is: Main.hs
330− other-modules:
331− default-extensions:
332− BangPatterns
333− DeriveFunctor
334− DeriveGeneric
335− GeneralizedNewtypeDeriving
336− LambdaCase
337− NamedFieldPuns
338− OverloadedStrings
339− RecordWildCards
340− ScopedTypeVariables
341− StandaloneDeriving
342− TupleSections
343− TypeApplications
344− ViewPatterns
hie.yaml+1−12
@@ -1,12 +1 @@
1−cradle:
2− cabal:
3− - path: "./src"
4− component: "ghcide:lib:ghcide"
5− - path: "./exe"
6− component: "ghcide:exe:ghcide"
7− - path: "./test"
8− component: "ghcide:test:ghcide-tests"
9− - path: "./bench"
10− component: "ghcide:benchmark:ghcide-bench"
11− - path: "./test/preprocessor"
12− component: "ghcide:exe:ghcide-test-preprocessor"
1+cradle: {stack: {component: "ghcide:lib"}}
src-ghc810/Development/IDE/GHC/HieAst.hsdeleted−1928
@@ -1,1928 +0,0 @@
1−
2−
3−{-
4−Forked from GHC v8.10.1 to work around the readFile side effect in mkHiefile
5−
6−Main functions for .hie file generation
7−-}
8−{- HLINT ignore -}
9−{-# LANGUAGE OverloadedStrings #-}
10−{-# LANGUAGE FlexibleInstances #-}
11−{-# LANGUAGE UndecidableInstances #-}
12−{-# LANGUAGE FlexibleContexts #-}
13−{-# LANGUAGE TypeSynonymInstances #-}
14−{-# LANGUAGE ScopedTypeVariables #-}
15−{-# LANGUAGE TypeFamilies #-}
16−{-# LANGUAGE TypeApplications #-}
17−{-# LANGUAGE AllowAmbiguousTypes #-}
18−{-# LANGUAGE ViewPatterns #-}
19−{-# LANGUAGE DeriveDataTypeable #-}
20−module Development.IDE.GHC.HieAst ( mkHieFile ) where
21−
22−import GhcPrelude
23−
24−import Avail ( Avails )
25−import Bag ( Bag, bagToList )
26−import BasicTypes
27−import BooleanFormula
28−import Class ( FunDep )
29−import CoreUtils ( exprType )
30−import ConLike ( conLikeName )
31−import Desugar ( deSugarExpr )
32−import FieldLabel
33−import GHC.Hs
34−import HscTypes
35−import Module ( ModuleName, ml_hs_file )
36−import MonadUtils ( concatMapM, liftIO )
37−import Name ( Name, nameSrcSpan, setNameLoc )
38−import NameEnv ( NameEnv, emptyNameEnv, extendNameEnv, lookupNameEnv )
39−import SrcLoc
40−import TcHsSyn ( hsLitType, hsPatType )
41−import Type ( mkVisFunTys, Type )
42−import TysWiredIn ( mkListTy, mkSumTy )
43−import Var ( Id, Var, setVarName, varName, varType )
44−import TcRnTypes
45−import MkIface ( mkIfaceExports )
46−import Panic
47−
48−import HieTypes
49−import HieUtils
50−
51−import qualified Data.Array as A
52−import qualified Data.ByteString as BS
53−import qualified Data.Map as M
54−import qualified Data.Set as S
55−import Data.Data ( Data, Typeable )
56−import Data.List ( foldl1' )
57−import Data.Maybe ( listToMaybe )
58−import Control.Monad.Trans.Reader
59−import Control.Monad.Trans.Class ( lift )
60−
61−{- Note [Updating HieAst for changes in the GHC AST]
62−
63−When updating the code in this file for changes in the GHC AST, you
64−need to pay attention to the following things:
65−
66−1) Symbols (Names/Vars/Modules) in the following categories:
67−
68− a) Symbols that appear in the source file that directly correspond to
69− something the user typed
70− b) Symbols that don't appear in the source, but should be in some sense
71− "visible" to a user, particularly via IDE tooling or the like. This
72− includes things like the names introduced by RecordWildcards (We record
73− all the names introduced by a (..) in HIE files), and will include implicit
74− parameters and evidence variables after one of my pending MRs lands.
75−
76−2) Subtrees that may contain such symbols, or correspond to a SrcSpan in
77− the file. This includes all `Located` things
78−
79−For 1), you need to call `toHie` for one of the following instances
80−
81−instance ToHie (Context (Located Name)) where ...
82−instance ToHie (Context (Located Var)) where ...
83−instance ToHie (IEContext (Located ModuleName)) where ...
84−
85−`Context` is a data type that looks like:
86−
87−data Context a = C ContextInfo a -- Used for names and bindings
88−
89−`ContextInfo` is defined in `HieTypes`, and looks like
90−
91−data ContextInfo
92− = Use -- ^ regular variable
93− | MatchBind
94− | IEThing IEType -- ^ import/export
95− | TyDecl
96− -- | Value binding
97− | ValBind
98− BindType -- ^ whether or not the binding is in an instance
99− Scope -- ^ scope over which the value is bound
100− (Maybe Span) -- ^ span of entire binding
101− ...
102−
103−It is used to annotate symbols in the .hie files with some extra information on
104−the context in which they occur and should be fairly self explanatory. You need
105−to select one that looks appropriate for the symbol usage. In very rare cases,
106−you might need to extend this sum type if none of the cases seem appropriate.
107−
108−So, given a `Located Name` that is just being "used", and not defined at a
109−particular location, you would do the following:
110−
111− toHie $ C Use located_name
112−
113−If you select one that corresponds to a binding site, you will need to
114−provide a `Scope` and a `Span` for your binding. Both of these are basically
115−`SrcSpans`.
116−
117−The `SrcSpan` in the `Scope` is supposed to span over the part of the source
118−where the symbol can be legally allowed to occur. For more details on how to
119−calculate this, see Note [Capturing Scopes and other non local information]
120−in HieAst.
121−
122−The binding `Span` is supposed to be the span of the entire binding for
123−the name.
124−
125−For a function definition `foo`:
126−
127−foo x = x + y
128− where y = x^2
129−
130−The binding `Span` is the span of the entire function definition from `foo x`
131−to `x^2`. For a class definition, this is the span of the entire class, and
132−so on. If this isn't well defined for your bit of syntax (like a variable
133−bound by a lambda), then you can just supply a `Nothing`
134−
135−There is a test that checks that all symbols in the resulting HIE file
136−occur inside their stated `Scope`. This can be turned on by passing the
137−-fvalidate-ide-info flag to ghc along with -fwrite-ide-info to generate the
138−.hie file.
139−
140−You may also want to provide a test in testsuite/test/hiefile that includes
141−a file containing your new construction, and tests that the calculated scope
142−is valid (by using -fvalidate-ide-info)
143−
144−For subtrees in the AST that may contain symbols, the procedure is fairly
145−straightforward. If you are extending the GHC AST, you will need to provide a
146−`ToHie` instance for any new types you may have introduced in the AST.
147−
148−Here are is an extract from the `ToHie` instance for (LHsExpr (GhcPass p)):
149−
150− toHie e@(L mspan oexpr) = concatM $ getTypeNode e : case oexpr of
151− HsVar _ (L _ var) ->
152− [ toHie $ C Use (L mspan var)
153− -- Patch up var location since typechecker removes it
154− ]
155− HsConLikeOut _ con ->
156− [ toHie $ C Use $ L mspan $ conLikeName con
157− ]
158− ...
159− HsApp _ a b ->
160− [ toHie a
161− , toHie b
162− ]
163−
164−If your subtree is `Located` or has a `SrcSpan` available, the output list
165−should contain a HieAst `Node` corresponding to the subtree. You can use
166−either `makeNode` or `getTypeNode` for this purpose, depending on whether it
167−makes sense to assign a `Type` to the subtree. After this, you just need
168−to concatenate the result of calling `toHie` on all subexpressions and
169−appropriately annotated symbols contained in the subtree.
170−
171−The code above from the ToHie instance of `LhsExpr (GhcPass p)` is supposed
172−to work for both the renamed and typechecked source. `getTypeNode` is from
173−the `HasType` class defined in this file, and it has different instances
174−for `GhcTc` and `GhcRn` that allow it to access the type of the expression
175−when given a typechecked AST:
176−
177−class Data a => HasType a where
178− getTypeNode :: a -> HieM [HieAST Type]
179−instance HasType (LHsExpr GhcTc) where
180− getTypeNode e@(L spn e') = ... -- Actually get the type for this expression
181−instance HasType (LHsExpr GhcRn) where
182− getTypeNode (L spn e) = makeNode e spn -- Fallback to a regular `makeNode` without recording the type
183−
184−If your subtree doesn't have a span available, you can omit the `makeNode`
185−call and just recurse directly in to the subexpressions.
186−
187−-}
188−
189−-- These synonyms match those defined in main/GHC.hs
190−type RenamedSource = ( HsGroup GhcRn, [LImportDecl GhcRn]
191− , Maybe [(LIE GhcRn, Avails)]
192− , Maybe LHsDocString )
193−type TypecheckedSource = LHsBinds GhcTc
194−
195−
196−{- Note [Name Remapping]
197−The Typechecker introduces new names for mono names in AbsBinds.
198−We don't care about the distinction between mono and poly bindings,
199−so we replace all occurrences of the mono name with the poly name.
200−-}
201−newtype HieState = HieState
202− { name_remapping :: NameEnv Id
203− }
204−
205−initState :: HieState
206−initState = HieState emptyNameEnv
207−
208−class ModifyState a where -- See Note [Name Remapping]
209− addSubstitution :: a -> a -> HieState -> HieState
210−
211−instance ModifyState Name where
212− addSubstitution _ _ hs = hs
213−
214−instance ModifyState Id where
215− addSubstitution mono poly hs =
216− hs{name_remapping = extendNameEnv (name_remapping hs) (varName mono) poly}
217−
218−modifyState :: ModifyState (IdP p) => [ABExport p] -> HieState -> HieState
219−modifyState = foldr go id
220− where
221− go ABE{abe_poly=poly,abe_mono=mono} f = addSubstitution mono poly . f
222− go _ f = f
223−
224−type HieM = ReaderT HieState Hsc
225−
226−-- | Construct an 'HieFile' from the outputs of the typechecker.
227−mkHieFile :: ModSummary
228− -> TcGblEnv
229− -> RenamedSource
230− -> BS.ByteString -> Hsc HieFile
231−mkHieFile ms ts rs src = do
232− let tc_binds = tcg_binds ts
233− (asts', arr) <- getCompressedAsts tc_binds rs
234− let Just src_file = ml_hs_file $ ms_location ms
235− return $ HieFile
236− { hie_hs_file = src_file
237− , hie_module = ms_mod ms
238− , hie_types = arr
239− , hie_asts = asts'
240− -- mkIfaceExports sorts the AvailInfos for stability
241− , hie_exports = mkIfaceExports (tcg_exports ts)
242− , hie_hs_src = src
243− }
244−
245−getCompressedAsts :: TypecheckedSource -> RenamedSource
246− -> Hsc (HieASTs TypeIndex, A.Array TypeIndex HieTypeFlat)
247−getCompressedAsts ts rs = do
248− asts <- enrichHie ts rs
249− return $ compressTypes asts
250−
251−enrichHie :: TypecheckedSource -> RenamedSource -> Hsc (HieASTs Type)
252−enrichHie ts (hsGrp, imports, exports, _) = flip runReaderT initState $ do
253− tasts <- toHie $ fmap (BC RegularBind ModuleScope) ts
254− rasts <- processGrp hsGrp
255− imps <- toHie $ filter (not . ideclImplicit . unLoc) imports
256− exps <- toHie $ fmap (map $ IEC Export . fst) exports
257− let spanFile children = case children of
258− [] -> mkRealSrcSpan (mkRealSrcLoc "" 1 1) (mkRealSrcLoc "" 1 1)
259− _ -> mkRealSrcSpan (realSrcSpanStart $ nodeSpan $ head children)
260− (realSrcSpanEnd $ nodeSpan $ last children)
261−
262− modulify xs =
263− Node (simpleNodeInfo "Module" "Module") (spanFile xs) xs
264−
265− asts = HieASTs
266− $ resolveTyVarScopes
267− $ M.map (modulify . mergeSortAsts)
268− $ M.fromListWith (++)
269− $ map (\x -> (srcSpanFile (nodeSpan x),[x])) flat_asts
270−
271− flat_asts = concat
272− [ tasts
273− , rasts
274− , imps
275− , exps
276− ]
277− return asts
278− where
279− processGrp grp = concatM
280− [ toHie $ fmap (RS ModuleScope ) hs_valds grp
281− , toHie $ hs_splcds grp
282− , toHie $ hs_tyclds grp
283− , toHie $ hs_derivds grp
284− , toHie $ hs_fixds grp
285− , toHie $ hs_defds grp
286− , toHie $ hs_fords grp
287− , toHie $ hs_warnds grp
288− , toHie $ hs_annds grp
289− , toHie $ hs_ruleds grp
290− ]
291−
292−getRealSpan :: SrcSpan -> Maybe Span
293−getRealSpan (RealSrcSpan sp) = Just sp
294−getRealSpan _ = Nothing
295−
296−grhss_span :: GRHSs p body -> SrcSpan
297−grhss_span (GRHSs _ xs bs) = foldl' combineSrcSpans (getLoc bs) (map getLoc xs)
298−grhss_span (XGRHSs _) = panic "XGRHS has no span"
299−
300−bindingsOnly :: [Context Name] -> [HieAST a]
301−bindingsOnly [] = []
302−bindingsOnly (C c n : xs) = case nameSrcSpan n of
303− RealSrcSpan span -> Node nodeinfo span [] : bindingsOnly xs
304− where nodeinfo = NodeInfo S.empty [] (M.singleton (Right n) info)
305− info = mempty{identInfo = S.singleton c}
306− _ -> bindingsOnly xs
307−
308−concatM :: Monad m => [m [a]] -> m [a]
309−concatM xs = concat <$> sequence xs
310−
311−{- Note [Capturing Scopes and other non local information]
312−toHie is a local tranformation, but scopes of bindings cannot be known locally,
313−hence we have to push the relevant info down into the binding nodes.
314−We use the following types (*Context and *Scoped) to wrap things and
315−carry the required info
316−(Maybe Span) always carries the span of the entire binding, including rhs
317−-}
318−data Context a = C ContextInfo a -- Used for names and bindings
319−
320−data RContext a = RC RecFieldContext a
321−data RFContext a = RFC RecFieldContext (Maybe Span) a
322−-- ^ context for record fields
323−
324−data IEContext a = IEC IEType a
325−-- ^ context for imports/exports
326−
327−data BindContext a = BC BindType Scope a
328−-- ^ context for imports/exports
329−
330−data PatSynFieldContext a = PSC (Maybe Span) a
331−-- ^ context for pattern synonym fields.
332−
333−data SigContext a = SC SigInfo a
334−-- ^ context for type signatures
335−
336−data SigInfo = SI SigType (Maybe Span)
337−
338−data SigType = BindSig | ClassSig | InstSig
339−
340−data RScoped a = RS Scope a
341−-- ^ Scope spans over everything to the right of a, (mostly) not
342−-- including a itself
343−-- (Includes a in a few special cases like recursive do bindings) or
344−-- let/where bindings
345−
346−-- | Pattern scope
347−data PScoped a = PS (Maybe Span)
348− Scope -- ^ use site of the pattern
349− Scope -- ^ pattern to the right of a, not including a
350− a
351− deriving (Typeable, Data) -- Pattern Scope
352−
353−{- Note [TyVar Scopes]
354−Due to -XScopedTypeVariables, type variables can be in scope quite far from
355−their original binding. We resolve the scope of these type variables
356−in a separate pass
357−-}
358−data TScoped a = TS TyVarScope a -- TyVarScope
359−
360−data TVScoped a = TVS TyVarScope Scope a -- TyVarScope
361−-- ^ First scope remains constant
362−-- Second scope is used to build up the scope of a tyvar over
363−-- things to its right, ala RScoped
364−
365−-- | Each element scopes over the elements to the right
366−listScopes :: Scope -> [Located a] -> [RScoped (Located a)]
367−listScopes _ [] = []
368−listScopes rhsScope [pat] = [RS rhsScope pat]
369−listScopes rhsScope (pat : pats) = RS sc pat : pats'
370− where
371− pats'@((RS scope p):_) = listScopes rhsScope pats
372− sc = combineScopes scope $ mkScope $ getLoc p
373−
374−-- | 'listScopes' specialised to 'PScoped' things
375−patScopes
376− :: Maybe Span
377− -> Scope
378− -> Scope
379− -> [LPat (GhcPass p)]
380− -> [PScoped (LPat (GhcPass p))]
381−patScopes rsp useScope patScope xs =
382− map (\(RS sc a) -> PS rsp useScope sc (composeSrcSpan a)) $
383− listScopes patScope (map dL xs)
384−
385−-- | 'listScopes' specialised to 'TVScoped' things
386−tvScopes
387− :: TyVarScope
388− -> Scope
389− -> [LHsTyVarBndr a]
390− -> [TVScoped (LHsTyVarBndr a)]
391−tvScopes tvScope rhsScope xs =
392− map (\(RS sc a)-> TVS tvScope sc a) $ listScopes rhsScope xs
393−
394−{- Note [Scoping Rules for SigPat]
395−Explicitly quantified variables in pattern type signatures are not
396−brought into scope in the rhs, but implicitly quantified variables
397−are (HsWC and HsIB).
398−This is unlike other signatures, where explicitly quantified variables
399−are brought into the RHS Scope
400−For example
401−foo :: forall a. ...;
402−foo = ... -- a is in scope here
403−
404−bar (x :: forall a. a -> a) = ... -- a is not in scope here
405−-- ^ a is in scope here (pattern body)
406−
407−bax (x :: a) = ... -- a is in scope here
408−Because of HsWC and HsIB pass on their scope to their children
409−we must wrap the LHsType in pattern signatures in a
410−Shielded explictly, so that the HsWC/HsIB scope is not passed
411−on the the LHsType
412−-}
413−
414−data Shielded a = SH Scope a -- Ignores its TScope, uses its own scope instead
415−
416−type family ProtectedSig a where
417− ProtectedSig GhcRn = HsWildCardBndrs GhcRn (HsImplicitBndrs
418− GhcRn
419− (Shielded (LHsType GhcRn)))
420− ProtectedSig GhcTc = NoExtField
421−
422−class ProtectSig a where
423− protectSig :: Scope -> LHsSigWcType (NoGhcTc a) -> ProtectedSig a
424−
425−instance (HasLoc a) => HasLoc (Shielded a) where
426− loc (SH _ a) = loc a
427−
428−instance (ToHie (TScoped a)) => ToHie (TScoped (Shielded a)) where
429− toHie (TS _ (SH sc a)) = toHie (TS (ResolvedScopes [sc]) a)
430−
431−instance ProtectSig GhcTc where
432− protectSig _ _ = noExtField
433−
434−instance ProtectSig GhcRn where
435− protectSig sc (HsWC a (HsIB b sig)) =
436− HsWC a (HsIB b (SH sc sig))
437− protectSig _ (HsWC _ (XHsImplicitBndrs nec)) = noExtCon nec
438− protectSig _ (XHsWildCardBndrs nec) = noExtCon nec
439−
440−class HasLoc a where
441− -- ^ defined so that HsImplicitBndrs and HsWildCardBndrs can
442− -- know what their implicit bindings are scoping over
443− loc :: a -> SrcSpan
444−
445−instance HasLoc thing => HasLoc (TScoped thing) where
446− loc (TS _ a) = loc a
447−
448−instance HasLoc thing => HasLoc (PScoped thing) where
449− loc (PS _ _ _ a) = loc a
450−
451−instance HasLoc (LHsQTyVars GhcRn) where
452− loc (HsQTvs _ vs) = loc vs
453− loc _ = noSrcSpan
454−
455−instance HasLoc thing => HasLoc (HsImplicitBndrs a thing) where
456− loc (HsIB _ a) = loc a
457− loc _ = noSrcSpan
458−
459−instance HasLoc thing => HasLoc (HsWildCardBndrs a thing) where
460− loc (HsWC _ a) = loc a
461− loc _ = noSrcSpan
462−
463−instance HasLoc (Located a) where
464− loc (L l _) = l
465−
466−instance HasLoc a => HasLoc [a] where
467− loc [] = noSrcSpan
468− loc xs = foldl1' combineSrcSpans $ map loc xs
469−
470−instance HasLoc a => HasLoc (FamEqn s a) where
471− loc (FamEqn _ a Nothing b _ c) = foldl1' combineSrcSpans [loc a, loc b, loc c]
472− loc (FamEqn _ a (Just tvs) b _ c) = foldl1' combineSrcSpans
473− [loc a, loc tvs, loc b, loc c]
474− loc _ = noSrcSpan
475−instance (HasLoc tm, HasLoc ty) => HasLoc (HsArg tm ty) where
476− loc (HsValArg tm) = loc tm
477− loc (HsTypeArg _ ty) = loc ty
478− loc (HsArgPar sp) = sp
479−
480−instance HasLoc (HsDataDefn GhcRn) where
481− loc def@(HsDataDefn{}) = loc $ dd_cons def
482− -- Only used for data family instances, so we only need rhs
483− -- Most probably the rest will be unhelpful anyway
484− loc _ = noSrcSpan
485−
486−{- Note [Real DataCon Name]
487−The typechecker subtitutes the conLikeWrapId for the name, but we don't want
488−this showing up in the hieFile, so we replace the name in the Id with the
489−original datacon name
490−See also Note [Data Constructor Naming]
491−-}
492−class HasRealDataConName p where
493− getRealDataCon :: XRecordCon p -> Located (IdP p) -> Located (IdP p)
494−
495−instance HasRealDataConName GhcRn where
496− getRealDataCon _ n = n
497−instance HasRealDataConName GhcTc where
498− getRealDataCon RecordConTc{rcon_con_like = con} (L sp var) =
499− L sp (setVarName var (conLikeName con))
500−
501−-- | The main worker class
502−-- See Note [Updating HieAst for changes in the GHC AST] for more information
503−-- on how to add/modify instances for this.
504−class ToHie a where
505− toHie :: a -> HieM [HieAST Type]
506−
507−-- | Used to collect type info
508−class Data a => HasType a where
509− getTypeNode :: a -> HieM [HieAST Type]
510−
511−instance (ToHie a) => ToHie [a] where
512− toHie = concatMapM toHie
513−
514−instance (ToHie a) => ToHie (Bag a) where
515− toHie = toHie . bagToList
516−
517−instance (ToHie a) => ToHie (Maybe a) where
518− toHie = maybe (pure []) toHie
519−
520−instance ToHie (Context (Located NoExtField)) where
521− toHie _ = pure []
522−
523−instance ToHie (TScoped NoExtField) where
524− toHie _ = pure []
525−
526−instance ToHie (IEContext (Located ModuleName)) where
527− toHie (IEC c (L (RealSrcSpan span) mname)) =
528− pure $ [Node (NodeInfo S.empty [] idents) span []]
529− where details = mempty{identInfo = S.singleton (IEThing c)}
530− idents = M.singleton (Left mname) details
531− toHie _ = pure []
532−
533−instance ToHie (Context (Located Var)) where
534− toHie c = case c of
535− C context (L (RealSrcSpan span) name')
536− -> do
537− m <- asks name_remapping
538− let name = case lookupNameEnv m (varName name') of
539− Just var -> var
540− Nothing-> name'
541− pure
542− [Node
543− (NodeInfo S.empty [] $
544− M.singleton (Right $ varName name)
545− (IdentifierDetails (Just $ varType name')
546− (S.singleton context)))
547− span
548− []]
549− _ -> pure []
550−
551−instance ToHie (Context (Located Name)) where
552− toHie c = case c of
553− C context (L (RealSrcSpan span) name') -> do
554− m <- asks name_remapping
555− let name = case lookupNameEnv m name' of
556− Just var -> varName var
557− Nothing -> name'
558− pure
559− [Node
560− (NodeInfo S.empty [] $
561− M.singleton (Right name)
562− (IdentifierDetails Nothing
563− (S.singleton context)))
564− span
565− []]
566− _ -> pure []
567−
568−-- | Dummy instances - never called
569−instance ToHie (TScoped (LHsSigWcType GhcTc)) where
570− toHie _ = pure []
571−instance ToHie (TScoped (LHsWcType GhcTc)) where
572− toHie _ = pure []
573−instance ToHie (SigContext (LSig GhcTc)) where
574− toHie _ = pure []
575−instance ToHie (TScoped Type) where
576− toHie _ = pure []
577−
578−instance HasType (LHsBind GhcRn) where
579− getTypeNode (L spn bind) = makeNode bind spn
580−
581−instance HasType (LHsBind GhcTc) where
582− getTypeNode (L spn bind) = case bind of
583− FunBind{fun_id = name} -> makeTypeNode bind spn (varType $ unLoc name)
584− _ -> makeNode bind spn
585−
586−instance HasType (Located (Pat GhcRn)) where
587− getTypeNode (dL -> L spn pat) = makeNode pat spn
588−
589−instance HasType (Located (Pat GhcTc)) where
590− getTypeNode (dL -> L spn opat) = makeTypeNode opat spn (hsPatType opat)
591−
592−instance HasType (LHsExpr GhcRn) where
593− getTypeNode (L spn e) = makeNode e spn
594−
595−-- | This instance tries to construct 'HieAST' nodes which include the type of
596−-- the expression. It is not yet possible to do this efficiently for all
597−-- expression forms, so we skip filling in the type for those inputs.
598−--
599−-- 'HsApp', for example, doesn't have any type information available directly on
600−-- the node. Our next recourse would be to desugar it into a 'CoreExpr' then
601−-- query the type of that. Yet both the desugaring call and the type query both
602−-- involve recursive calls to the function and argument! This is particularly
603−-- problematic when you realize that the HIE traversal will eventually visit
604−-- those nodes too and ask for their types again.
605−--
606−-- Since the above is quite costly, we just skip cases where computing the
607−-- expression's type is going to be expensive.
608−--
609−-- See #16233
610−instance HasType (LHsExpr GhcTc) where
611− getTypeNode e@(L spn e') = lift $
612− -- Some expression forms have their type immediately available
613− let tyOpt = case e' of
614− HsLit _ l -> Just (hsLitType l)
615− HsOverLit _ o -> Just (overLitType o)
616−
617− HsLam _ (MG { mg_ext = groupTy }) -> Just (matchGroupType groupTy)
618− HsLamCase _ (MG { mg_ext = groupTy }) -> Just (matchGroupType groupTy)
619− HsCase _ _ (MG { mg_ext = groupTy }) -> Just (mg_res_ty groupTy)
620−
621− ExplicitList ty _ _ -> Just (mkListTy ty)
622− ExplicitSum ty _ _ _ -> Just (mkSumTy ty)
623− HsDo ty _ _ -> Just ty
624− HsMultiIf ty _ -> Just ty
625−
626− _ -> Nothing
627−
628− in
629− case tyOpt of
630− Just t -> makeTypeNode e' spn t
631− Nothing
632− | skipDesugaring e' -> fallback
633− | otherwise -> do
634− hs_env <- Hsc $ \e w -> return (e,w)
635− (_,mbe) <- liftIO $ deSugarExpr hs_env e
636− maybe fallback (makeTypeNode e' spn . exprType) mbe
637− where
638− fallback = makeNode e' spn
639−
640− matchGroupType :: MatchGroupTc -> Type
641− matchGroupType (MatchGroupTc args res) = mkVisFunTys args res
642−
643− -- | Skip desugaring of these expressions for performance reasons.
644− --
645− -- See impact on Haddock output (esp. missing type annotations or links)
646− -- before marking more things here as 'False'. See impact on Haddock
647− -- performance before marking more things as 'True'.
648− skipDesugaring :: HsExpr a -> Bool
649− skipDesugaring e = case e of
650− HsVar{} -> False
651− HsUnboundVar{} -> False
652− HsConLikeOut{} -> False
653− HsRecFld{} -> False
654− HsOverLabel{} -> False
655− HsIPVar{} -> False
656− HsWrap{} -> False
657− _ -> True
658−
659−instance ( ToHie (Context (Located (IdP a)))
660− , ToHie (MatchGroup a (LHsExpr a))
661− , ToHie (PScoped (LPat a))
662− , ToHie (GRHSs a (LHsExpr a))
663− , ToHie (LHsExpr a)
664− , ToHie (Located (PatSynBind a a))
665− , HasType (LHsBind a)
666− , ModifyState (IdP a)
667− , Data (HsBind a)
668− ) => ToHie (BindContext (LHsBind a)) where
669− toHie (BC context scope b@(L span bind)) =
670− concatM $ getTypeNode b : case bind of
671− FunBind{fun_id = name, fun_matches = matches} ->
672− [ toHie $ C (ValBind context scope $ getRealSpan span) name
673− , toHie matches
674− ]
675− PatBind{pat_lhs = lhs, pat_rhs = rhs} ->
676− [ toHie $ PS (getRealSpan span) scope NoScope lhs
677− , toHie rhs
678− ]
679− VarBind{var_rhs = expr} ->
680− [ toHie expr
681− ]
682− AbsBinds{abs_exports = xs, abs_binds = binds} ->
683− [ local (modifyState xs) $ -- Note [Name Remapping]
684− toHie $ fmap (BC context scope) binds
685− ]
686− PatSynBind _ psb ->
687− [ toHie $ L span psb -- PatSynBinds only occur at the top level
688− ]
689− XHsBindsLR _ -> []
690−
691−instance ( ToHie (LMatch a body)
692− ) => ToHie (MatchGroup a body) where
693− toHie mg = concatM $ case mg of
694− MG{ mg_alts = (L span alts) , mg_origin = FromSource } ->
695− [ pure $ locOnly span
696− , toHie alts
697− ]
698− MG{} -> []
699− XMatchGroup _ -> []
700−
701−instance ( ToHie (Context (Located (IdP a)))
702− , ToHie (PScoped (LPat a))
703− , ToHie (HsPatSynDir a)
704− ) => ToHie (Located (PatSynBind a a)) where
705− toHie (L sp psb) = concatM $ case psb of
706− PSB{psb_id=var, psb_args=dets, psb_def=pat, psb_dir=dir} ->
707− [ toHie $ C (Decl PatSynDec $ getRealSpan sp) var
708− , toHie $ toBind dets
709− , toHie $ PS Nothing lhsScope NoScope pat
710− , toHie dir
711− ]
712− where
713− lhsScope = combineScopes varScope detScope
714− varScope = mkLScope var
715− detScope = case dets of
716− (PrefixCon args) -> foldr combineScopes NoScope $ map mkLScope args
717− (InfixCon a b) -> combineScopes (mkLScope a) (mkLScope b)
718− (RecCon r) -> foldr go NoScope r
719− go (RecordPatSynField a b) c = combineScopes c
720− $ combineScopes (mkLScope a) (mkLScope b)
721− detSpan = case detScope of
722− LocalScope a -> Just a
723− _ -> Nothing
724− toBind (PrefixCon args) = PrefixCon $ map (C Use) args
725− toBind (InfixCon a b) = InfixCon (C Use a) (C Use b)
726− toBind (RecCon r) = RecCon $ map (PSC detSpan) r
727− XPatSynBind _ -> []
728−
729−instance ( ToHie (MatchGroup a (LHsExpr a))
730− ) => ToHie (HsPatSynDir a) where
731− toHie dir = case dir of
732− ExplicitBidirectional mg -> toHie mg
733− _ -> pure []
734−
735−instance ( a ~ GhcPass p
736− , ToHie body
737− , ToHie (HsMatchContext (NameOrRdrName (IdP a)))
738− , ToHie (PScoped (LPat a))
739− , ToHie (GRHSs a body)
740− , Data (Match a body)
741− ) => ToHie (LMatch (GhcPass p) body) where
742− toHie (L span m ) = concatM $ makeNode m span : case m of
743− Match{m_ctxt=mctx, m_pats = pats, m_grhss = grhss } ->
744− [ toHie mctx
745− , let rhsScope = mkScope $ grhss_span grhss
746− in toHie $ patScopes Nothing rhsScope NoScope pats
747− , toHie grhss
748− ]
749− XMatch _ -> []
750−
751−instance ( ToHie (Context (Located a))
752− ) => ToHie (HsMatchContext a) where
753− toHie (FunRhs{mc_fun=name}) = toHie $ C MatchBind name
754− toHie (StmtCtxt a) = toHie a
755− toHie _ = pure []
756−
757−instance ( ToHie (HsMatchContext a)
758− ) => ToHie (HsStmtContext a) where
759− toHie (PatGuard a) = toHie a
760− toHie (ParStmtCtxt a) = toHie a
761− toHie (TransStmtCtxt a) = toHie a
762− toHie _ = pure []
763−
764−instance ( a ~ GhcPass p
765− , ToHie (Context (Located (IdP a)))
766− , ToHie (RContext (HsRecFields a (PScoped (LPat a))))
767− , ToHie (LHsExpr a)
768− , ToHie (TScoped (LHsSigWcType a))
769− , ProtectSig a
770− , ToHie (TScoped (ProtectedSig a))
771− , HasType (LPat a)
772− , Data (HsSplice a)
773− ) => ToHie (PScoped (Located (Pat (GhcPass p)))) where
774− toHie (PS rsp scope pscope lpat@(dL -> L ospan opat)) =
775− concatM $ getTypeNode lpat : case opat of
776− WildPat _ ->
777− []
778− VarPat _ lname ->
779− [ toHie $ C (PatternBind scope pscope rsp) lname
780− ]
781− LazyPat _ p ->
782− [ toHie $ PS rsp scope pscope p
783− ]
784− AsPat _ lname pat ->
785− [ toHie $ C (PatternBind scope
786− (combineScopes (mkLScope (dL pat)) pscope)
787− rsp)
788− lname
789− , toHie $ PS rsp scope pscope pat
790− ]
791− ParPat _ pat ->
792− [ toHie $ PS rsp scope pscope pat
793− ]
794− BangPat _ pat ->
795− [ toHie $ PS rsp scope pscope pat
796− ]
797− ListPat _ pats ->
798− [ toHie $ patScopes rsp scope pscope pats
799− ]
800− TuplePat _ pats _ ->
801− [ toHie $ patScopes rsp scope pscope pats
802− ]
803− SumPat _ pat _ _ ->
804− [ toHie $ PS rsp scope pscope pat
805− ]
806− ConPatIn c dets ->
807− [ toHie $ C Use c
808− , toHie $ contextify dets
809− ]
810− ConPatOut {pat_con = con, pat_args = dets}->
811− [ toHie $ C Use $ fmap conLikeName con
812− , toHie $ contextify dets
813− ]
814− ViewPat _ expr pat ->
815− [ toHie expr
816− , toHie $ PS rsp scope pscope pat
817− ]
818− SplicePat _ sp ->
819− [ toHie $ L ospan sp
820− ]
821− LitPat _ _ ->
822− []
823− NPat _ _ _ _ ->
824− []
825− NPlusKPat _ n _ _ _ _ ->
826− [ toHie $ C (PatternBind scope pscope rsp) n
827− ]
828− SigPat _ pat sig ->
829− [ toHie $ PS rsp scope pscope pat
830− , let cscope = mkLScope (dL pat) in
831− toHie $ TS (ResolvedScopes [cscope, scope, pscope])
832− (protectSig @a cscope sig)
833− -- See Note [Scoping Rules for SigPat]
834− ]
835− CoPat _ _ _ _ ->
836− []
837− XPat _ -> []
838− where
839− contextify (PrefixCon args) = PrefixCon $ patScopes rsp scope pscope args
840− contextify (InfixCon a b) = InfixCon a' b'
841− where [a', b'] = patScopes rsp scope pscope [a,b]
842− contextify (RecCon r) = RecCon $ RC RecFieldMatch $ contextify_rec r
843− contextify_rec (HsRecFields fds a) = HsRecFields (map go scoped_fds) a
844− where
845− go (RS fscope (L spn (HsRecField lbl pat pun))) =
846− L spn $ HsRecField lbl (PS rsp scope fscope pat) pun
847− scoped_fds = listScopes pscope fds
848−
849−instance ( ToHie body
850− , ToHie (LGRHS a body)
851− , ToHie (RScoped (LHsLocalBinds a))
852− ) => ToHie (GRHSs a body) where
853− toHie grhs = concatM $ case grhs of
854− GRHSs _ grhss binds ->
855− [ toHie grhss
856− , toHie $ RS (mkScope $ grhss_span grhs) binds
857− ]
858− XGRHSs _ -> []
859−
860−instance ( ToHie (Located body)
861− , ToHie (RScoped (GuardLStmt a))
862− , Data (GRHS a (Located body))
863− ) => ToHie (LGRHS a (Located body)) where
864− toHie (L span g) = concatM $ makeNode g span : case g of
865− GRHS _ guards body ->
866− [ toHie $ listScopes (mkLScope body) guards
867− , toHie body
868− ]
869− XGRHS _ -> []
870−
871−instance ( a ~ GhcPass p
872− , ToHie (Context (Located (IdP a)))
873− , HasType (LHsExpr a)
874− , ToHie (PScoped (LPat a))
875− , ToHie (MatchGroup a (LHsExpr a))
876− , ToHie (LGRHS a (LHsExpr a))
877− , ToHie (RContext (HsRecordBinds a))
878− , ToHie (RFContext (Located (AmbiguousFieldOcc a)))
879− , ToHie (ArithSeqInfo a)
880− , ToHie (LHsCmdTop a)
881− , ToHie (RScoped (GuardLStmt a))
882− , ToHie (RScoped (LHsLocalBinds a))
883− , ToHie (TScoped (LHsWcType (NoGhcTc a)))
884− , ToHie (TScoped (LHsSigWcType (NoGhcTc a)))
885− , Data (HsExpr a)
886− , Data (HsSplice a)
887− , Data (HsTupArg a)
888− , Data (AmbiguousFieldOcc a)
889− , (HasRealDataConName a)
890− ) => ToHie (LHsExpr (GhcPass p)) where
891− toHie e@(L mspan oexpr) = concatM $ getTypeNode e : case oexpr of
892− HsVar _ (L _ var) ->
893− [ toHie $ C Use (L mspan var)
894− -- Patch up var location since typechecker removes it
895− ]
896− HsUnboundVar _ _ ->
897− []
898− HsConLikeOut _ con ->
899− [ toHie $ C Use $ L mspan $ conLikeName con
900− ]
901− HsRecFld _ fld ->
902− [ toHie $ RFC RecFieldOcc Nothing (L mspan fld)
903− ]
904− HsOverLabel _ _ _ -> []
905− HsIPVar _ _ -> []
906− HsOverLit _ _ -> []
907− HsLit _ _ -> []
908− HsLam _ mg ->
909− [ toHie mg
910− ]
911− HsLamCase _ mg ->
912− [ toHie mg
913− ]
914− HsApp _ a b ->
915− [ toHie a
916− , toHie b
917− ]
918− HsAppType _ expr sig ->
919− [ toHie expr
920− , toHie $ TS (ResolvedScopes []) sig
921− ]
922− OpApp _ a b c ->
923− [ toHie a
924− , toHie b
925− , toHie c
926− ]
927− NegApp _ a _ ->
928− [ toHie a
929− ]
930− HsPar _ a ->
931− [ toHie a
932− ]
933− SectionL _ a b ->
934− [ toHie a
935− , toHie b
936− ]
937− SectionR _ a b ->
938− [ toHie a
939− , toHie b
940− ]
941− ExplicitTuple _ args _ ->
942− [ toHie args
943− ]
944− ExplicitSum _ _ _ expr ->
945− [ toHie expr
946− ]
947− HsCase _ expr matches ->
948− [ toHie expr
949− , toHie matches
950− ]
951− HsIf _ _ a b c ->
952− [ toHie a
953− , toHie b
954− , toHie c
955− ]
956− HsMultiIf _ grhss ->
957− [ toHie grhss
958− ]
959− HsLet _ binds expr ->
960− [ toHie $ RS (mkLScope expr) binds
961− , toHie expr
962− ]
963− HsDo _ _ (L ispan stmts) ->
964− [ pure $ locOnly ispan
965− , toHie $ listScopes NoScope stmts
966− ]
967− ExplicitList _ _ exprs ->
968− [ toHie exprs
969− ]
970− RecordCon {rcon_ext = mrealcon, rcon_con_name = name, rcon_flds = binds} ->
971− [ toHie $ C Use (getRealDataCon @a mrealcon name)
972− -- See Note [Real DataCon Name]
973− , toHie $ RC RecFieldAssign $ binds
974− ]
975− RecordUpd {rupd_expr = expr, rupd_flds = upds}->
976− [ toHie expr
977− , toHie $ map (RC RecFieldAssign) upds
978− ]
979− ExprWithTySig _ expr sig ->
980− [ toHie expr
981− , toHie $ TS (ResolvedScopes [mkLScope expr]) sig
982− ]
983− ArithSeq _ _ info ->
984− [ toHie info
985− ]
986− HsSCC _ _ _ expr ->
987− [ toHie expr
988− ]
989− HsCoreAnn _ _ _ expr ->
990− [ toHie expr
991− ]
992− HsProc _ pat cmdtop ->
993− [ toHie $ PS Nothing (mkLScope cmdtop) NoScope pat
994− , toHie cmdtop
995− ]
996− HsStatic _ expr ->
997− [ toHie expr
998− ]
999− HsTick _ _ expr ->
1000− [ toHie expr
1001− ]
1002− HsBinTick _ _ _ expr ->
1003− [ toHie expr
1004− ]
1005− HsTickPragma _ _ _ _ expr ->
1006− [ toHie expr
1007− ]
1008− HsWrap _ _ a ->
1009− [ toHie $ L mspan a
1010− ]
1011− HsBracket _ b ->
1012− [ toHie b
1013− ]
1014− HsRnBracketOut _ b p ->
1015− [ toHie b
1016− , toHie p
1017− ]
1018− HsTcBracketOut _ b p ->
1019− [ toHie b
1020− , toHie p
1021− ]
1022− HsSpliceE _ x ->
1023− [ toHie $ L mspan x
1024− ]
1025− XExpr _ -> []
1026−
1027−instance ( a ~ GhcPass p
1028− , ToHie (LHsExpr a)
1029− , Data (HsTupArg a)
1030− ) => ToHie (LHsTupArg (GhcPass p)) where
1031− toHie (L span arg) = concatM $ makeNode arg span : case arg of
1032− Present _ expr ->
1033− [ toHie expr
1034− ]
1035− Missing _ -> []
1036− XTupArg _ -> []
1037−
1038−instance ( a ~ GhcPass p
1039− , ToHie (PScoped (LPat a))
1040− , ToHie (LHsExpr a)
1041− , ToHie (SigContext (LSig a))
1042− , ToHie (RScoped (LHsLocalBinds a))
1043− , ToHie (RScoped (ApplicativeArg a))
1044− , ToHie (Located body)
1045− , Data (StmtLR a a (Located body))
1046− , Data (StmtLR a a (Located (HsExpr a)))
1047− ) => ToHie (RScoped (LStmt (GhcPass p) (Located body))) where
1048− toHie (RS scope (L span stmt)) = concatM $ makeNode stmt span : case stmt of
1049− LastStmt _ body _ _ ->
1050− [ toHie body
1051− ]
1052− BindStmt _ pat body _ _ ->
1053− [ toHie $ PS (getRealSpan $ getLoc body) scope NoScope pat
1054− , toHie body
1055− ]
1056− ApplicativeStmt _ stmts _ ->
1057− [ concatMapM (toHie . RS scope . snd) stmts
1058− ]
1059− BodyStmt _ body _ _ ->
1060− [ toHie body
1061− ]
1062− LetStmt _ binds ->
1063− [ toHie $ RS scope binds
1064− ]
1065− ParStmt _ parstmts _ _ ->
1066− [ concatMapM (\(ParStmtBlock _ stmts _ _) ->
1067− toHie $ listScopes NoScope stmts)
1068− parstmts
1069− ]
1070− TransStmt {trS_stmts = stmts, trS_using = using, trS_by = by} ->
1071− [ toHie $ listScopes scope stmts
1072− , toHie using
1073− , toHie by
1074− ]
1075− RecStmt {recS_stmts = stmts} ->
1076− [ toHie $ map (RS $ combineScopes scope (mkScope span)) stmts
1077− ]
1078− XStmtLR _ -> []
1079−
1080−instance ( ToHie (LHsExpr a)
1081− , ToHie (PScoped (LPat a))
1082− , ToHie (BindContext (LHsBind a))
1083− , ToHie (SigContext (LSig a))
1084− , ToHie (RScoped (HsValBindsLR a a))
1085− , Data (HsLocalBinds a)
1086− ) => ToHie (RScoped (LHsLocalBinds a)) where
1087− toHie (RS scope (L sp binds)) = concatM $ makeNode binds sp : case binds of
1088− EmptyLocalBinds _ -> []
1089− HsIPBinds _ _ -> []
1090− HsValBinds _ valBinds ->
1091− [ toHie $ RS (combineScopes scope $ mkScope sp)
1092− valBinds
1093− ]
1094− XHsLocalBindsLR _ -> []
1095−
1096−instance ( ToHie (BindContext (LHsBind a))
1097− , ToHie (SigContext (LSig a))
1098− , ToHie (RScoped (XXValBindsLR a a))
1099− ) => ToHie (RScoped (HsValBindsLR a a)) where
1100− toHie (RS sc v) = concatM $ case v of
1101− ValBinds _ binds sigs ->
1102− [ toHie $ fmap (BC RegularBind sc) binds
1103− , toHie $ fmap (SC (SI BindSig Nothing)) sigs
1104− ]
1105− XValBindsLR x -> [ toHie $ RS sc x ]
1106−
1107−instance ToHie (RScoped (NHsValBindsLR GhcTc)) where
1108− toHie (RS sc (NValBinds binds sigs)) = concatM $
1109− [ toHie (concatMap (map (BC RegularBind sc) . bagToList . snd) binds)
1110− , toHie $ fmap (SC (SI BindSig Nothing)) sigs
1111− ]
1112−instance ToHie (RScoped (NHsValBindsLR GhcRn)) where
1113− toHie (RS sc (NValBinds binds sigs)) = concatM $
1114− [ toHie (concatMap (map (BC RegularBind sc) . bagToList . snd) binds)
1115− , toHie $ fmap (SC (SI BindSig Nothing)) sigs
1116− ]
1117−
1118−instance ( ToHie (RContext (LHsRecField a arg))
1119− ) => ToHie (RContext (HsRecFields a arg)) where
1120− toHie (RC c (HsRecFields fields _)) = toHie $ map (RC c) fields
1121−
1122−instance ( ToHie (RFContext (Located label))
1123− , ToHie arg
1124− , HasLoc arg
1125− , Data label
1126− , Data arg
1127− ) => ToHie (RContext (LHsRecField' label arg)) where
1128− toHie (RC c (L span recfld)) = concatM $ makeNode recfld span : case recfld of
1129− HsRecField label expr _ ->
1130− [ toHie $ RFC c (getRealSpan $ loc expr) label
1131− , toHie expr
1132− ]
1133−
1134−removeDefSrcSpan :: Name -> Name
1135−removeDefSrcSpan n = setNameLoc n noSrcSpan
1136−
1137−instance ToHie (RFContext (LFieldOcc GhcRn)) where
1138− toHie (RFC c rhs (L nspan f)) = concatM $ case f of
1139− FieldOcc name _ ->
1140− [ toHie $ C (RecField c rhs) (L nspan $ removeDefSrcSpan name)
1141− ]
1142− XFieldOcc _ -> []
1143−
1144−instance ToHie (RFContext (LFieldOcc GhcTc)) where
1145− toHie (RFC c rhs (L nspan f)) = concatM $ case f of
1146− FieldOcc var _ ->
1147− let var' = setVarName var (removeDefSrcSpan $ varName var)
1148− in [ toHie $ C (RecField c rhs) (L nspan var')
1149− ]
1150− XFieldOcc _ -> []
1151−
1152−instance ToHie (RFContext (Located (AmbiguousFieldOcc GhcRn))) where
1153− toHie (RFC c rhs (L nspan afo)) = concatM $ case afo of
1154− Unambiguous name _ ->
1155− [ toHie $ C (RecField c rhs) $ L nspan $ removeDefSrcSpan name
1156− ]
1157− Ambiguous _name _ ->
1158− [ ]
1159− XAmbiguousFieldOcc _ -> []
1160−
1161−instance ToHie (RFContext (Located (AmbiguousFieldOcc GhcTc))) where
1162− toHie (RFC c rhs (L nspan afo)) = concatM $ case afo of
1163− Unambiguous var _ ->
1164− let var' = setVarName var (removeDefSrcSpan $ varName var)
1165− in [ toHie $ C (RecField c rhs) (L nspan var')
1166− ]
1167− Ambiguous var _ ->
1168− let var' = setVarName var (removeDefSrcSpan $ varName var)
1169− in [ toHie $ C (RecField c rhs) (L nspan var')
1170− ]
1171− XAmbiguousFieldOcc _ -> []
1172−
1173−instance ( a ~ GhcPass p
1174− , ToHie (PScoped (LPat a))
1175− , ToHie (BindContext (LHsBind a))
1176− , ToHie (LHsExpr a)
1177− , ToHie (SigContext (LSig a))
1178− , ToHie (RScoped (HsValBindsLR a a))
1179− , Data (StmtLR a a (Located (HsExpr a)))
1180− , Data (HsLocalBinds a)
1181− ) => ToHie (RScoped (ApplicativeArg (GhcPass p))) where
1182− toHie (RS sc (ApplicativeArgOne _ pat expr _ _)) = concatM
1183− [ toHie $ PS Nothing sc NoScope pat
1184− , toHie expr
1185− ]
1186− toHie (RS sc (ApplicativeArgMany _ stmts _ pat)) = concatM
1187− [ toHie $ listScopes NoScope stmts
1188− , toHie $ PS Nothing sc NoScope pat
1189− ]
1190− toHie (RS _ (XApplicativeArg _)) = pure []
1191−
1192−instance (ToHie arg, ToHie rec) => ToHie (HsConDetails arg rec) where
1193− toHie (PrefixCon args) = toHie args
1194− toHie (RecCon rec) = toHie rec
1195− toHie (InfixCon a b) = concatM [ toHie a, toHie b]
1196−
1197−instance ( ToHie (LHsCmd a)
1198− , Data (HsCmdTop a)
1199− ) => ToHie (LHsCmdTop a) where
1200− toHie (L span top) = concatM $ makeNode top span : case top of
1201− HsCmdTop _ cmd ->
1202− [ toHie cmd
1203− ]
1204− XCmdTop _ -> []
1205−
1206−instance ( a ~ GhcPass p
1207− , ToHie (PScoped (LPat a))
1208− , ToHie (BindContext (LHsBind a))
1209− , ToHie (LHsExpr a)
1210− , ToHie (MatchGroup a (LHsCmd a))
1211− , ToHie (SigContext (LSig a))
1212− , ToHie (RScoped (HsValBindsLR a a))
1213− , Data (HsCmd a)
1214− , Data (HsCmdTop a)
1215− , Data (StmtLR a a (Located (HsCmd a)))
1216− , Data (HsLocalBinds a)
1217− , Data (StmtLR a a (Located (HsExpr a)))
1218− ) => ToHie (LHsCmd (GhcPass p)) where
1219− toHie (L span cmd) = concatM $ makeNode cmd span : case cmd of
1220− HsCmdArrApp _ a b _ _ ->
1221− [ toHie a
1222− , toHie b
1223− ]
1224− HsCmdArrForm _ a _ _ cmdtops ->
1225− [ toHie a
1226− , toHie cmdtops
1227− ]
1228− HsCmdApp _ a b ->
1229− [ toHie a
1230− , toHie b
1231− ]
1232− HsCmdLam _ mg ->
1233− [ toHie mg
1234− ]
1235− HsCmdPar _ a ->
1236− [ toHie a
1237− ]
1238− HsCmdCase _ expr alts ->
1239− [ toHie expr
1240− , toHie alts
1241− ]
1242− HsCmdIf _ _ a b c ->
1243− [ toHie a
1244− , toHie b
1245− , toHie c
1246− ]
1247− HsCmdLet _ binds cmd' ->
1248− [ toHie $ RS (mkLScope cmd') binds
1249− , toHie cmd'
1250− ]
1251− HsCmdDo _ (L ispan stmts) ->
1252− [ pure $ locOnly ispan
1253− , toHie $ listScopes NoScope stmts
1254− ]
1255− HsCmdWrap _ _ _ -> []
1256− XCmd _ -> []
1257−
1258−instance ToHie (TyClGroup GhcRn) where
1259− toHie TyClGroup{ group_tyclds = classes
1260− , group_roles = roles
1261− , group_kisigs = sigs
1262− , group_instds = instances } =
1263− concatM
1264− [ toHie classes
1265− , toHie sigs
1266− , toHie roles
1267− , toHie instances
1268− ]
1269− toHie (XTyClGroup _) = pure []
1270−
1271−instance ToHie (LTyClDecl GhcRn) where
1272− toHie (L span decl) = concatM $ makeNode decl span : case decl of
1273− FamDecl {tcdFam = fdecl} ->
1274− [ toHie (L span fdecl)
1275− ]
1276− SynDecl {tcdLName = name, tcdTyVars = vars, tcdRhs = typ} ->
1277− [ toHie $ C (Decl SynDec $ getRealSpan span) name
1278− , toHie $ TS (ResolvedScopes [mkScope $ getLoc typ]) vars
1279− , toHie typ
1280− ]
1281− DataDecl {tcdLName = name, tcdTyVars = vars, tcdDataDefn = defn} ->
1282− [ toHie $ C (Decl DataDec $ getRealSpan span) name
1283− , toHie $ TS (ResolvedScopes [quant_scope, rhs_scope]) vars
1284− , toHie defn
1285− ]
1286− where
1287− quant_scope = mkLScope $ dd_ctxt defn
1288− rhs_scope = sig_sc `combineScopes` con_sc `combineScopes` deriv_sc
1289− sig_sc = maybe NoScope mkLScope $ dd_kindSig defn
1290− con_sc = foldr combineScopes NoScope $ map mkLScope $ dd_cons defn
1291− deriv_sc = mkLScope $ dd_derivs defn
1292− ClassDecl { tcdCtxt = context
1293− , tcdLName = name
1294− , tcdTyVars = vars
1295− , tcdFDs = deps
1296− , tcdSigs = sigs
1297− , tcdMeths = meths
1298− , tcdATs = typs
1299− , tcdATDefs = deftyps
1300− } ->
1301− [ toHie $ C (Decl ClassDec $ getRealSpan span) name
1302− , toHie context
1303− , toHie $ TS (ResolvedScopes [context_scope, rhs_scope]) vars
1304− , toHie deps
1305− , toHie $ map (SC $ SI ClassSig $ getRealSpan span) sigs
1306− , toHie $ fmap (BC InstanceBind ModuleScope) meths
1307− , toHie typs
1308− , concatMapM (pure . locOnly . getLoc) deftyps
1309− , toHie deftyps
1310− ]
1311− where
1312− context_scope = mkLScope context
1313− rhs_scope = foldl1' combineScopes $ map mkScope
1314− [ loc deps, loc sigs, loc (bagToList meths), loc typs, loc deftyps]
1315− XTyClDecl _ -> []
1316−
1317−instance ToHie (LFamilyDecl GhcRn) where
1318− toHie (L span decl) = concatM $ makeNode decl span : case decl of
1319− FamilyDecl _ info name vars _ sig inj ->
1320− [ toHie $ C (Decl FamDec $ getRealSpan span) name
1321− , toHie $ TS (ResolvedScopes [rhsSpan]) vars
1322− , toHie info
1323− , toHie $ RS injSpan sig
1324− , toHie inj
1325− ]
1326− where
1327− rhsSpan = sigSpan `combineScopes` injSpan
1328− sigSpan = mkScope $ getLoc sig
1329− injSpan = maybe NoScope (mkScope . getLoc) inj
1330− XFamilyDecl _ -> []
1331−
1332−instance ToHie (FamilyInfo GhcRn) where
1333− toHie (ClosedTypeFamily (Just eqns)) = concatM $
1334− [ concatMapM (pure . locOnly . getLoc) eqns
1335− , toHie $ map go eqns
1336− ]
1337− where
1338− go (L l ib) = TS (ResolvedScopes [mkScope l]) ib
1339− toHie _ = pure []
1340−
1341−instance ToHie (RScoped (LFamilyResultSig GhcRn)) where
1342− toHie (RS sc (L span sig)) = concatM $ makeNode sig span : case sig of
1343− NoSig _ ->
1344− []
1345− KindSig _ k ->
1346− [ toHie k
1347− ]
1348− TyVarSig _ bndr ->
1349− [ toHie $ TVS (ResolvedScopes [sc]) NoScope bndr
1350− ]
1351− XFamilyResultSig _ -> []
1352−
1353−instance ToHie (Located (FunDep (Located Name))) where
1354− toHie (L span fd@(lhs, rhs)) = concatM $
1355− [ makeNode fd span
1356− , toHie $ map (C Use) lhs
1357− , toHie $ map (C Use) rhs
1358− ]
1359−
1360−instance (ToHie rhs, HasLoc rhs)
1361− => ToHie (TScoped (FamEqn GhcRn rhs)) where
1362− toHie (TS _ f) = toHie f
1363−
1364−instance (ToHie rhs, HasLoc rhs)
1365− => ToHie (FamEqn GhcRn rhs) where
1366− toHie fe@(FamEqn _ var tybndrs pats _ rhs) = concatM $
1367− [ toHie $ C (Decl InstDec $ getRealSpan $ loc fe) var
1368− , toHie $ fmap (tvScopes (ResolvedScopes []) scope) tybndrs
1369− , toHie pats
1370− , toHie rhs
1371− ]
1372− where scope = combineScopes patsScope rhsScope
1373− patsScope = mkScope (loc pats)
1374− rhsScope = mkScope (loc rhs)
1375− toHie (XFamEqn _) = pure []
1376−
1377−instance ToHie (LInjectivityAnn GhcRn) where
1378− toHie (L span ann) = concatM $ makeNode ann span : case ann of
1379− InjectivityAnn lhs rhs ->
1380− [ toHie $ C Use lhs
1381− , toHie $ map (C Use) rhs
1382− ]
1383−
1384−instance ToHie (HsDataDefn GhcRn) where
1385− toHie (HsDataDefn _ _ ctx _ mkind cons derivs) = concatM
1386− [ toHie ctx
1387− , toHie mkind
1388− , toHie cons
1389− , toHie derivs
1390− ]
1391− toHie (XHsDataDefn _) = pure []
1392−
1393−instance ToHie (HsDeriving GhcRn) where
1394− toHie (L span clauses) = concatM
1395− [ pure $ locOnly span
1396− , toHie clauses
1397− ]
1398−
1399−instance ToHie (LHsDerivingClause GhcRn) where
1400− toHie (L span cl) = concatM $ makeNode cl span : case cl of
1401− HsDerivingClause _ strat (L ispan tys) ->
1402− [ toHie strat
1403− , pure $ locOnly ispan
1404− , toHie $ map (TS (ResolvedScopes [])) tys
1405− ]
1406− XHsDerivingClause _ -> []
1407−
1408−instance ToHie (Located (DerivStrategy GhcRn)) where
1409− toHie (L span strat) = concatM $ makeNode strat span : case strat of
1410− StockStrategy -> []
1411− AnyclassStrategy -> []
1412− NewtypeStrategy -> []
1413− ViaStrategy s -> [ toHie $ TS (ResolvedScopes []) s ]
1414−
1415−instance ToHie (Located OverlapMode) where
1416− toHie (L span _) = pure $ locOnly span
1417−
1418−instance ToHie (LConDecl GhcRn) where
1419− toHie (L span decl) = concatM $ makeNode decl span : case decl of
1420− ConDeclGADT { con_names = names, con_qvars = qvars
1421− , con_mb_cxt = ctx, con_args = args, con_res_ty = typ } ->
1422− [ toHie $ map (C (Decl ConDec $ getRealSpan span)) names
1423− , toHie $ TS (ResolvedScopes [ctxScope, rhsScope]) qvars
1424− , toHie ctx
1425− , toHie args
1426− , toHie typ
1427− ]
1428− where
1429− rhsScope = combineScopes argsScope tyScope
1430− ctxScope = maybe NoScope mkLScope ctx
1431− argsScope = condecl_scope args
1432− tyScope = mkLScope typ
1433− ConDeclH98 { con_name = name, con_ex_tvs = qvars
1434− , con_mb_cxt = ctx, con_args = dets } ->
1435− [ toHie $ C (Decl ConDec $ getRealSpan span) name
1436− , toHie $ tvScopes (ResolvedScopes []) rhsScope qvars
1437− , toHie ctx
1438− , toHie dets
1439− ]
1440− where
1441− rhsScope = combineScopes ctxScope argsScope
1442− ctxScope = maybe NoScope mkLScope ctx
1443− argsScope = condecl_scope dets
1444− XConDecl _ -> []
1445− where condecl_scope args = case args of
1446− PrefixCon xs -> foldr combineScopes NoScope $ map mkLScope xs
1447− InfixCon a b -> combineScopes (mkLScope a) (mkLScope b)
1448− RecCon x -> mkLScope x
1449−
1450−instance ToHie (Located [LConDeclField GhcRn]) where
1451− toHie (L span decls) = concatM $
1452− [ pure $ locOnly span
1453− , toHie decls
1454− ]
1455−
1456−instance ( HasLoc thing
1457− , ToHie (TScoped thing)
1458− ) => ToHie (TScoped (HsImplicitBndrs GhcRn thing)) where
1459− toHie (TS sc (HsIB ibrn a)) = concatM $
1460− [ pure $ bindingsOnly $ map (C $ TyVarBind (mkScope span) sc) ibrn
1461− , toHie $ TS sc a
1462− ]
1463− where span = loc a
1464− toHie (TS _ (XHsImplicitBndrs _)) = pure []
1465−
1466−instance ( HasLoc thing
1467− , ToHie (TScoped thing)
1468− ) => ToHie (TScoped (HsWildCardBndrs GhcRn thing)) where
1469− toHie (TS sc (HsWC names a)) = concatM $
1470− [ pure $ bindingsOnly $ map (C $ TyVarBind (mkScope span) sc) names
1471− , toHie $ TS sc a
1472− ]
1473− where span = loc a
1474− toHie (TS _ (XHsWildCardBndrs _)) = pure []
1475−
1476−instance ToHie (LStandaloneKindSig GhcRn) where
1477− toHie (L sp sig) = concatM [makeNode sig sp, toHie sig]
1478−
1479−instance ToHie (StandaloneKindSig GhcRn) where
1480− toHie sig = concatM $ case sig of
1481− StandaloneKindSig _ name typ ->
1482− [ toHie $ C TyDecl name
1483− , toHie $ TS (ResolvedScopes []) typ
1484− ]
1485− XStandaloneKindSig _ -> []
1486−
1487−instance ToHie (SigContext (LSig GhcRn)) where
1488− toHie (SC (SI styp msp) (L sp sig)) = concatM $ makeNode sig sp : case sig of
1489− TypeSig _ names typ ->
1490− [ toHie $ map (C TyDecl) names
1491− , toHie $ TS (UnresolvedScope (map unLoc names) Nothing) typ
1492− ]
1493− PatSynSig _ names typ ->
1494− [ toHie $ map (C TyDecl) names
1495− , toHie $ TS (UnresolvedScope (map unLoc names) Nothing) typ
1496− ]
1497− ClassOpSig _ _ names typ ->
1498− [ case styp of
1499− ClassSig -> toHie $ map (C $ ClassTyDecl $ getRealSpan sp) names
1500− _ -> toHie $ map (C $ TyDecl) names
1501− , toHie $ TS (UnresolvedScope (map unLoc names) msp) typ
1502− ]
1503− IdSig _ _ -> []
1504− FixSig _ fsig ->
1505− [ toHie $ L sp fsig
1506− ]
1507− InlineSig _ name _ ->
1508− [ toHie $ (C Use) name
1509− ]
1510− SpecSig _ name typs _ ->
1511− [ toHie $ (C Use) name
1512− , toHie $ map (TS (ResolvedScopes [])) typs
1513− ]
1514− SpecInstSig _ _ typ ->
1515− [ toHie $ TS (ResolvedScopes []) typ
1516− ]
1517− MinimalSig _ _ form ->
1518− [ toHie form
1519− ]
1520− SCCFunSig _ _ name mtxt ->
1521− [ toHie $ (C Use) name
1522− , pure $ maybe [] (locOnly . getLoc) mtxt
1523− ]
1524− CompleteMatchSig _ _ (L ispan names) typ ->
1525− [ pure $ locOnly ispan
1526− , toHie $ map (C Use) names
1527− , toHie $ fmap (C Use) typ
1528− ]
1529− XSig _ -> []
1530−
1531−instance ToHie (LHsType GhcRn) where
1532− toHie x = toHie $ TS (ResolvedScopes []) x
1533−
1534−instance ToHie (TScoped (LHsType GhcRn)) where
1535− toHie (TS tsc (L span t)) = concatM $ makeNode t span : case t of
1536− HsForAllTy _ _ bndrs body ->
1537− [ toHie $ tvScopes tsc (mkScope $ getLoc body) bndrs
1538− , toHie body
1539− ]
1540− HsQualTy _ ctx body ->
1541− [ toHie ctx
1542− , toHie body
1543− ]
1544− HsTyVar _ _ var ->
1545− [ toHie $ C Use var
1546− ]
1547− HsAppTy _ a b ->
1548− [ toHie a
1549− , toHie b
1550− ]
1551− HsAppKindTy _ ty ki ->
1552− [ toHie ty
1553− , toHie $ TS (ResolvedScopes []) ki
1554− ]
1555− HsFunTy _ a b ->
1556− [ toHie a
1557− , toHie b
1558− ]
1559− HsListTy _ a ->
1560− [ toHie a
1561− ]
1562− HsTupleTy _ _ tys ->
1563− [ toHie tys
1564− ]
1565− HsSumTy _ tys ->
1566− [ toHie tys
1567− ]
1568− HsOpTy _ a op b ->
1569− [ toHie a
1570− , toHie $ C Use op
1571− , toHie b
1572− ]
1573− HsParTy _ a ->
1574− [ toHie a
1575− ]
1576− HsIParamTy _ ip ty ->
1577− [ toHie ip
1578− , toHie ty
1579− ]
1580− HsKindSig _ a b ->
1581− [ toHie a
1582− , toHie b
1583− ]
1584− HsSpliceTy _ a ->
1585− [ toHie $ L span a
1586− ]
1587− HsDocTy _ a _ ->
1588− [ toHie a
1589− ]
1590− HsBangTy _ _ ty ->
1591− [ toHie ty
1592− ]
1593− HsRecTy _ fields ->
1594− [ toHie fields
1595− ]
1596− HsExplicitListTy _ _ tys ->
1597− [ toHie tys
1598− ]
1599− HsExplicitTupleTy _ tys ->
1600− [ toHie tys
1601− ]
1602− HsTyLit _ _ -> []
1603− HsWildCardTy _ -> []
1604− HsStarTy _ _ -> []
1605− XHsType _ -> []
1606−
1607−instance (ToHie tm, ToHie ty) => ToHie (HsArg tm ty) where
1608− toHie (HsValArg tm) = toHie tm
1609− toHie (HsTypeArg _ ty) = toHie ty
1610− toHie (HsArgPar sp) = pure $ locOnly sp
1611−
1612−instance ToHie (TVScoped (LHsTyVarBndr GhcRn)) where
1613− toHie (TVS tsc sc (L span bndr)) = concatM $ makeNode bndr span : case bndr of
1614− UserTyVar _ var ->
1615− [ toHie $ C (TyVarBind sc tsc) var
1616− ]
1617− KindedTyVar _ var kind ->
1618− [ toHie $ C (TyVarBind sc tsc) var
1619− , toHie kind
1620− ]
1621− XTyVarBndr _ -> []
1622−
1623−instance ToHie (TScoped (LHsQTyVars GhcRn)) where
1624− toHie (TS sc (HsQTvs implicits vars)) = concatM $
1625− [ pure $ bindingsOnly bindings
1626− , toHie $ tvScopes sc NoScope vars
1627− ]
1628− where
1629− varLoc = loc vars
1630− bindings = map (C $ TyVarBind (mkScope varLoc) sc) implicits
1631− toHie (TS _ (XLHsQTyVars _)) = pure []
1632−
1633−instance ToHie (LHsContext GhcRn) where
1634− toHie (L span tys) = concatM $
1635− [ pure $ locOnly span
1636− , toHie tys
1637− ]
1638−
1639−instance ToHie (LConDeclField GhcRn) where
1640− toHie (L span field) = concatM $ makeNode field span : case field of
1641− ConDeclField _ fields typ _ ->
1642− [ toHie $ map (RFC RecFieldDecl (getRealSpan $ loc typ)) fields
1643− , toHie typ
1644− ]
1645− XConDeclField _ -> []
1646−
1647−instance ToHie (LHsExpr a) => ToHie (ArithSeqInfo a) where
1648− toHie (From expr) = toHie expr
1649− toHie (FromThen a b) = concatM $
1650− [ toHie a
1651− , toHie b
1652− ]
1653− toHie (FromTo a b) = concatM $
1654− [ toHie a
1655− , toHie b
1656− ]
1657− toHie (FromThenTo a b c) = concatM $
1658− [ toHie a
1659− , toHie b
1660− , toHie c
1661− ]
1662−
1663−instance ToHie (LSpliceDecl GhcRn) where
1664− toHie (L span decl) = concatM $ makeNode decl span : case decl of
1665− SpliceDecl _ splice _ ->
1666− [ toHie splice
1667− ]
1668− XSpliceDecl _ -> []
1669−
1670−instance ToHie (HsBracket a) where
1671− toHie _ = pure []
1672−
1673−instance ToHie PendingRnSplice where
1674− toHie _ = pure []
1675−
1676−instance ToHie PendingTcSplice where
1677− toHie _ = pure []
1678−
1679−instance ToHie (LBooleanFormula (Located Name)) where
1680− toHie (L span form) = concatM $ makeNode form span : case form of
1681− Var a ->
1682− [ toHie $ C Use a
1683− ]
1684− And forms ->
1685− [ toHie forms
1686− ]
1687− Or forms ->
1688− [ toHie forms
1689− ]
1690− Parens f ->
1691− [ toHie f
1692− ]
1693−
1694−instance ToHie (Located HsIPName) where
1695− toHie (L span e) = makeNode e span
1696−
1697−instance ( ToHie (LHsExpr a)
1698− , Data (HsSplice a)
1699− ) => ToHie (Located (HsSplice a)) where
1700− toHie (L span sp) = concatM $ makeNode sp span : case sp of
1701− HsTypedSplice _ _ _ expr ->
1702− [ toHie expr
1703− ]
1704− HsUntypedSplice _ _ _ expr ->
1705− [ toHie expr
1706− ]
1707− HsQuasiQuote _ _ _ ispan _ ->
1708− [ pure $ locOnly ispan
1709− ]
1710− HsSpliced _ _ _ ->
1711− []
1712− HsSplicedT _ ->
1713− []
1714− XSplice _ -> []
1715−
1716−instance ToHie (LRoleAnnotDecl GhcRn) where
1717− toHie (L span annot) = concatM $ makeNode annot span : case annot of
1718− RoleAnnotDecl _ var roles ->
1719− [ toHie $ C Use var
1720− , concatMapM (pure . locOnly . getLoc) roles
1721− ]
1722− XRoleAnnotDecl _ -> []
1723−
1724−instance ToHie (LInstDecl GhcRn) where
1725− toHie (L span decl) = concatM $ makeNode decl span : case decl of
1726− ClsInstD _ d ->
1727− [ toHie $ L span d
1728− ]
1729− DataFamInstD _ d ->
1730− [ toHie $ L span d
1731− ]
1732− TyFamInstD _ d ->
1733− [ toHie $ L span d
1734− ]
1735− XInstDecl _ -> []
1736−
1737−instance ToHie (LClsInstDecl GhcRn) where
1738− toHie (L span decl) = concatM
1739− [ toHie $ TS (ResolvedScopes [mkScope span]) $ cid_poly_ty decl
1740− , toHie $ fmap (BC InstanceBind ModuleScope) $ cid_binds decl
1741− , toHie $ map (SC $ SI InstSig $ getRealSpan span) $ cid_sigs decl
1742− , pure $ concatMap (locOnly . getLoc) $ cid_tyfam_insts decl
1743− , toHie $ cid_tyfam_insts decl
1744− , pure $ concatMap (locOnly . getLoc) $ cid_datafam_insts decl
1745− , toHie $ cid_datafam_insts decl
1746− , toHie $ cid_overlap_mode decl
1747− ]
1748−
1749−instance ToHie (LDataFamInstDecl GhcRn) where
1750− toHie (L sp (DataFamInstDecl d)) = toHie $ TS (ResolvedScopes [mkScope sp]) d
1751−
1752−instance ToHie (LTyFamInstDecl GhcRn) where
1753− toHie (L sp (TyFamInstDecl d)) = toHie $ TS (ResolvedScopes [mkScope sp]) d
1754−
1755−instance ToHie (Context a)
1756− => ToHie (PatSynFieldContext (RecordPatSynField a)) where
1757− toHie (PSC sp (RecordPatSynField a b)) = concatM $
1758− [ toHie $ C (RecField RecFieldDecl sp) a
1759− , toHie $ C Use b
1760− ]
1761−
1762−instance ToHie (LDerivDecl GhcRn) where
1763− toHie (L span decl) = concatM $ makeNode decl span : case decl of
1764− DerivDecl _ typ strat overlap ->
1765− [ toHie $ TS (ResolvedScopes []) typ
1766− , toHie strat
1767− , toHie overlap
1768− ]
1769− XDerivDecl _ -> []
1770−
1771−instance ToHie (LFixitySig GhcRn) where
1772− toHie (L span sig) = concatM $ makeNode sig span : case sig of
1773− FixitySig _ vars _ ->
1774− [ toHie $ map (C Use) vars
1775− ]
1776− XFixitySig _ -> []
1777−
1778−instance ToHie (LDefaultDecl GhcRn) where
1779− toHie (L span decl) = concatM $ makeNode decl span : case decl of
1780− DefaultDecl _ typs ->
1781− [ toHie typs
1782− ]
1783− XDefaultDecl _ -> []
1784−
1785−instance ToHie (LForeignDecl GhcRn) where
1786− toHie (L span decl) = concatM $ makeNode decl span : case decl of
1787− ForeignImport {fd_name = name, fd_sig_ty = sig, fd_fi = fi} ->
1788− [ toHie $ C (ValBind RegularBind ModuleScope $ getRealSpan span) name
1789− , toHie $ TS (ResolvedScopes []) sig
1790− , toHie fi
1791− ]
1792− ForeignExport {fd_name = name, fd_sig_ty = sig, fd_fe = fe} ->
1793− [ toHie $ C Use name
1794− , toHie $ TS (ResolvedScopes []) sig
1795− , toHie fe
1796− ]
1797− XForeignDecl _ -> []
1798−
1799−instance ToHie ForeignImport where
1800− toHie (CImport (L a _) (L b _) _ _ (L c _)) = pure $ concat $
1801− [ locOnly a
1802− , locOnly b
1803− , locOnly c
1804− ]
1805−
1806−instance ToHie ForeignExport where
1807− toHie (CExport (L a _) (L b _)) = pure $ concat $
1808− [ locOnly a
1809− , locOnly b
1810− ]
1811−
1812−instance ToHie (LWarnDecls GhcRn) where
1813− toHie (L span decl) = concatM $ makeNode decl span : case decl of
1814− Warnings _ _ warnings ->
1815− [ toHie warnings
1816− ]
1817− XWarnDecls _ -> []
1818−
1819−instance ToHie (LWarnDecl GhcRn) where
1820− toHie (L span decl) = concatM $ makeNode decl span : case decl of
1821− Warning _ vars _ ->
1822− [ toHie $ map (C Use) vars
1823− ]
1824− XWarnDecl _ -> []
1825−
1826−instance ToHie (LAnnDecl GhcRn) where
1827− toHie (L span decl) = concatM $ makeNode decl span : case decl of
1828− HsAnnotation _ _ prov expr ->
1829− [ toHie prov
1830− , toHie expr
1831− ]
1832− XAnnDecl _ -> []
1833−
1834−instance ToHie (Context (Located a)) => ToHie (AnnProvenance a) where
1835− toHie (ValueAnnProvenance a) = toHie $ C Use a
1836− toHie (TypeAnnProvenance a) = toHie $ C Use a
1837− toHie ModuleAnnProvenance = pure []
1838−
1839−instance ToHie (LRuleDecls GhcRn) where
1840− toHie (L span decl) = concatM $ makeNode decl span : case decl of
1841− HsRules _ _ rules ->
1842− [ toHie rules
1843− ]
1844− XRuleDecls _ -> []
1845−
1846−instance ToHie (LRuleDecl GhcRn) where
1847− toHie (L _ (XRuleDecl _)) = pure []
1848− toHie (L span r@(HsRule _ rname _ tybndrs bndrs exprA exprB)) = concatM
1849− [ makeNode r span
1850− , pure $ locOnly $ getLoc rname
1851− , toHie $ fmap (tvScopes (ResolvedScopes []) scope) tybndrs
1852− , toHie $ map (RS $ mkScope span) bndrs
1853− , toHie exprA
1854− , toHie exprB
1855− ]
1856− where scope = bndrs_sc `combineScopes` exprA_sc `combineScopes` exprB_sc
1857− bndrs_sc = maybe NoScope mkLScope (listToMaybe bndrs)
1858− exprA_sc = mkLScope exprA
1859− exprB_sc = mkLScope exprB
1860−
1861−instance ToHie (RScoped (LRuleBndr GhcRn)) where
1862− toHie (RS sc (L span bndr)) = concatM $ makeNode bndr span : case bndr of
1863− RuleBndr _ var ->
1864− [ toHie $ C (ValBind RegularBind sc Nothing) var
1865− ]
1866− RuleBndrSig _ var typ ->
1867− [ toHie $ C (ValBind RegularBind sc Nothing) var
1868− , toHie $ TS (ResolvedScopes [sc]) typ
1869− ]
1870− XRuleBndr _ -> []
1871−
1872−instance ToHie (LImportDecl GhcRn) where
1873− toHie (L span decl) = concatM $ makeNode decl span : case decl of
1874− ImportDecl { ideclName = name, ideclAs = as, ideclHiding = hidden } ->
1875− [ toHie $ IEC Import name
1876− , toHie $ fmap (IEC ImportAs) as
1877− , maybe (pure []) goIE hidden
1878− ]
1879− XImportDecl _ -> []
1880− where
1881− goIE (hiding, (L sp liens)) = concatM $
1882− [ pure $ locOnly sp
1883− , toHie $ map (IEC c) liens
1884− ]
1885− where
1886− c = if hiding then ImportHiding else Import
1887−
1888−instance ToHie (IEContext (LIE GhcRn)) where
1889− toHie (IEC c (L span ie)) = concatM $ makeNode ie span : case ie of
1890− IEVar _ n ->
1891− [ toHie $ IEC c n
1892− ]
1893− IEThingAbs _ n ->
1894− [ toHie $ IEC c n
1895− ]
1896− IEThingAll _ n ->
1897− [ toHie $ IEC c n
1898− ]
1899− IEThingWith _ n _ ns flds ->
1900− [ toHie $ IEC c n
1901− , toHie $ map (IEC c) ns
1902− , toHie $ map (IEC c) flds
1903− ]
1904− IEModuleContents _ n ->
1905− [ toHie $ IEC c n
1906− ]
1907− IEGroup _ _ _ -> []
1908− IEDoc _ _ -> []
1909− IEDocNamed _ _ -> []
1910− XIE _ -> []
1911−
1912−instance ToHie (IEContext (LIEWrappedName Name)) where
1913− toHie (IEC c (L span iewn)) = concatM $ makeNode iewn span : case iewn of
1914− IEName n ->
1915− [ toHie $ C (IEThing c) n
1916− ]
1917− IEPattern p ->
1918− [ toHie $ C (IEThing c) p
1919− ]
1920− IEType n ->
1921− [ toHie $ C (IEThing c) n
1922− ]
1923−
1924−instance ToHie (IEContext (Located (FieldLbl Name))) where
1925− toHie (IEC c (L span lbl)) = concatM $ makeNode lbl span : case lbl of
1926− FieldLabel _ _ n ->
1927− [ toHie $ C (IEThing c) $ L span n
1928− ]
src-ghc88/Development/IDE/GHC/HieAst.hsdeleted−1789
@@ -1,1789 +0,0 @@
1−
2−{-
3−Forked from GHC v8.8.1 to work around the readFile side effect in mkHiefile
4−
5−Main functions for .hie file generation
6−-}
7−{- HLINT ignore -}
8−{-# LANGUAGE OverloadedStrings #-}
9−{-# LANGUAGE FlexibleInstances #-}
10−{-# LANGUAGE UndecidableInstances #-}
11−{-# LANGUAGE FlexibleContexts #-}
12−{-# LANGUAGE TypeSynonymInstances #-}
13−{-# LANGUAGE ScopedTypeVariables #-}
14−{-# LANGUAGE TypeFamilies #-}
15−{-# LANGUAGE TypeApplications #-}
16−{-# LANGUAGE AllowAmbiguousTypes #-}
17−{-# LANGUAGE ViewPatterns #-}
18−{-# LANGUAGE DeriveDataTypeable #-}
19−module Development.IDE.GHC.HieAst ( mkHieFile ) where
20−
21−import Avail ( Avails )
22−import Bag ( Bag, bagToList )
23−import BasicTypes
24−import BooleanFormula
25−import Class ( FunDep )
26−import CoreUtils ( exprType )
27−import ConLike ( conLikeName )
28−import Desugar ( deSugarExpr )
29−import FieldLabel
30−import HsSyn
31−import HscTypes
32−import Module ( ModuleName, ml_hs_file )
33−import MonadUtils ( concatMapM, liftIO )
34−import Name ( Name, nameSrcSpan, setNameLoc )
35−import SrcLoc
36−import TcHsSyn ( hsLitType, hsPatType )
37−import Type ( mkFunTys, Type )
38−import TysWiredIn ( mkListTy, mkSumTy )
39−import Var ( Id, Var, setVarName, varName, varType )
40−import TcRnTypes
41−import MkIface ( mkIfaceExports )
42−
43−import HieTypes
44−import HieUtils
45−
46−import qualified Data.Array as A
47−import qualified Data.ByteString as BS
48−import qualified Data.Map as M
49−import qualified Data.Set as S
50−import Data.Data ( Data, Typeable )
51−import Data.List (foldl', foldl1' )
52−import Data.Maybe ( listToMaybe )
53−import Control.Monad.Trans.Reader
54−import Control.Monad.Trans.Class ( lift )
55−
56−-- These synonyms match those defined in main/GHC.hs
57−type RenamedSource = ( HsGroup GhcRn, [LImportDecl GhcRn]
58− , Maybe [(LIE GhcRn, Avails)]
59− , Maybe LHsDocString )
60−type TypecheckedSource = LHsBinds GhcTc
61−
62−
63−{- Note [Name Remapping]
64−The Typechecker introduces new names for mono names in AbsBinds.
65−We don't care about the distinction between mono and poly bindings,
66−so we replace all occurrences of the mono name with the poly name.
67−-}
68−newtype HieState = HieState
69− { name_remapping :: M.Map Name Id
70− }
71−
72−initState :: HieState
73−initState = HieState M.empty
74−
75−class ModifyState a where -- See Note [Name Remapping]
76− addSubstitution :: a -> a -> HieState -> HieState
77−
78−instance ModifyState Name where
79− addSubstitution _ _ hs = hs
80−
81−instance ModifyState Id where
82− addSubstitution mono poly hs =
83− hs{name_remapping = M.insert (varName mono) poly (name_remapping hs)}
84−
85−modifyState :: ModifyState (IdP p) => [ABExport p] -> HieState -> HieState
86−modifyState = foldr go id
87− where
88− go ABE{abe_poly=poly,abe_mono=mono} f = addSubstitution mono poly . f
89− go _ f = f
90−
91−type HieM = ReaderT HieState Hsc
92−
93−-- | Construct an 'HieFile' from the outputs of the typechecker.
94−mkHieFile :: ModSummary
95− -> TcGblEnv
96− -> RenamedSource
97− -> BS.ByteString
98− -> Hsc HieFile
99−mkHieFile ms ts rs src = do
100− let tc_binds = tcg_binds ts
101− (asts', arr) <- getCompressedAsts tc_binds rs
102− let Just src_file = ml_hs_file $ ms_location ms
103− return $ HieFile
104− { hie_hs_file = src_file
105− , hie_module = ms_mod ms
106− , hie_types = arr
107− , hie_asts = asts'
108− -- mkIfaceExports sorts the AvailInfos for stability
109− , hie_exports = mkIfaceExports (tcg_exports ts)
110− , hie_hs_src = src
111− }
112−
113−getCompressedAsts :: TypecheckedSource -> RenamedSource
114− -> Hsc (HieASTs TypeIndex, A.Array TypeIndex HieTypeFlat)
115−getCompressedAsts ts rs = do
116− asts <- enrichHie ts rs
117− return $ compressTypes asts
118−
119−enrichHie :: TypecheckedSource -> RenamedSource -> Hsc (HieASTs Type)
120−enrichHie ts (hsGrp, imports, exports, _) = flip runReaderT initState $ do
121− tasts <- toHie $ fmap (BC RegularBind ModuleScope) ts
122− rasts <- processGrp hsGrp
123− imps <- toHie $ filter (not . ideclImplicit . unLoc) imports
124− exps <- toHie $ fmap (map $ IEC Export . fst) exports
125− let spanFile children = case children of
126− [] -> mkRealSrcSpan (mkRealSrcLoc "" 1 1) (mkRealSrcLoc "" 1 1)
127− _ -> mkRealSrcSpan (realSrcSpanStart $ nodeSpan $ head children)
128− (realSrcSpanEnd $ nodeSpan $ last children)
129−
130− modulify xs =
131− Node (simpleNodeInfo "Module" "Module") (spanFile xs) xs
132−
133− asts = HieASTs
134− $ resolveTyVarScopes
135− $ M.map (modulify . mergeSortAsts)
136− $ M.fromListWith (++)
137− $ map (\x -> (srcSpanFile (nodeSpan x),[x])) flat_asts
138−
139− flat_asts = concat
140− [ tasts
141− , rasts
142− , imps
143− , exps
144− ]
145− return asts
146− where
147− processGrp grp = concatM
148− [ toHie $ fmap (RS ModuleScope ) hs_valds grp
149− , toHie $ hs_splcds grp
150− , toHie $ hs_tyclds grp
151− , toHie $ hs_derivds grp
152− , toHie $ hs_fixds grp
153− , toHie $ hs_defds grp
154− , toHie $ hs_fords grp
155− , toHie $ hs_warnds grp
156− , toHie $ hs_annds grp
157− , toHie $ hs_ruleds grp
158− ]
159−
160−getRealSpan :: SrcSpan -> Maybe Span
161−getRealSpan (RealSrcSpan sp) = Just sp
162−getRealSpan _ = Nothing
163−
164−grhss_span :: GRHSs p body -> SrcSpan
165−grhss_span (GRHSs _ xs bs) = foldl' combineSrcSpans (getLoc bs) (map getLoc xs)
166−grhss_span (XGRHSs _) = error "XGRHS has no span"
167−
168−bindingsOnly :: [Context Name] -> [HieAST a]
169−bindingsOnly [] = []
170−bindingsOnly (C c n : xs) = case nameSrcSpan n of
171− RealSrcSpan span -> Node nodeinfo span [] : bindingsOnly xs
172− where nodeinfo = NodeInfo S.empty [] (M.singleton (Right n) info)
173− info = mempty{identInfo = S.singleton c}
174− _ -> bindingsOnly xs
175−
176−concatM :: Monad m => [m [a]] -> m [a]
177−concatM xs = concat <$> sequence xs
178−
179−{- Note [Capturing Scopes and other non local information]
180−toHie is a local tranformation, but scopes of bindings cannot be known locally,
181−hence we have to push the relevant info down into the binding nodes.
182−We use the following types (*Context and *Scoped) to wrap things and
183−carry the required info
184−(Maybe Span) always carries the span of the entire binding, including rhs
185−-}
186−data Context a = C ContextInfo a -- Used for names and bindings
187−
188−data RContext a = RC RecFieldContext a
189−data RFContext a = RFC RecFieldContext (Maybe Span) a
190−-- ^ context for record fields
191−
192−data IEContext a = IEC IEType a
193−-- ^ context for imports/exports
194−
195−data BindContext a = BC BindType Scope a
196−-- ^ context for imports/exports
197−
198−data PatSynFieldContext a = PSC (Maybe Span) a
199−-- ^ context for pattern synonym fields.
200−
201−data SigContext a = SC SigInfo a
202−-- ^ context for type signatures
203−
204−data SigInfo = SI SigType (Maybe Span)
205−
206−data SigType = BindSig | ClassSig | InstSig
207−
208−data RScoped a = RS Scope a
209−-- ^ Scope spans over everything to the right of a, (mostly) not
210−-- including a itself
211−-- (Includes a in a few special cases like recursive do bindings) or
212−-- let/where bindings
213−
214−-- | Pattern scope
215−data PScoped a = PS (Maybe Span)
216− Scope -- ^ use site of the pattern
217− Scope -- ^ pattern to the right of a, not including a
218− a
219− deriving (Typeable, Data) -- Pattern Scope
220−
221−{- Note [TyVar Scopes]
222−Due to -XScopedTypeVariables, type variables can be in scope quite far from
223−their original binding. We resolve the scope of these type variables
224−in a separate pass
225−-}
226−data TScoped a = TS TyVarScope a -- TyVarScope
227−
228−data TVScoped a = TVS TyVarScope Scope a -- TyVarScope
229−-- ^ First scope remains constant
230−-- Second scope is used to build up the scope of a tyvar over
231−-- things to its right, ala RScoped
232−
233−-- | Each element scopes over the elements to the right
234−listScopes :: Scope -> [Located a] -> [RScoped (Located a)]
235−listScopes _ [] = []
236−listScopes rhsScope [pat] = [RS rhsScope pat]
237−listScopes rhsScope (pat : pats) = RS sc pat : pats'
238− where
239− pats'@((RS scope p):_) = listScopes rhsScope pats
240− sc = combineScopes scope $ mkScope $ getLoc p
241−
242−-- | 'listScopes' specialised to 'PScoped' things
243−patScopes
244− :: Maybe Span
245− -> Scope
246− -> Scope
247− -> [LPat (GhcPass p)]
248− -> [PScoped (LPat (GhcPass p))]
249−patScopes rsp useScope patScope xs =
250− map (\(RS sc a) -> PS rsp useScope sc (unLoc a)) $
251− listScopes patScope (map dL xs)
252−
253−-- | 'listScopes' specialised to 'TVScoped' things
254−tvScopes
255− :: TyVarScope
256− -> Scope
257− -> [LHsTyVarBndr a]
258− -> [TVScoped (LHsTyVarBndr a)]
259−tvScopes tvScope rhsScope xs =
260− map (\(RS sc a)-> TVS tvScope sc a) $ listScopes rhsScope xs
261−
262−{- Note [Scoping Rules for SigPat]
263−Explicitly quantified variables in pattern type signatures are not
264−brought into scope in the rhs, but implicitly quantified variables
265−are (HsWC and HsIB).
266−This is unlike other signatures, where explicitly quantified variables
267−are brought into the RHS Scope
268−For example
269−foo :: forall a. ...;
270−foo = ... -- a is in scope here
271−
272−bar (x :: forall a. a -> a) = ... -- a is not in scope here
273−-- ^ a is in scope here (pattern body)
274−
275−bax (x :: a) = ... -- a is in scope here
276−Because of HsWC and HsIB pass on their scope to their children
277−we must wrap the LHsType in pattern signatures in a
278−Shielded explictly, so that the HsWC/HsIB scope is not passed
279−on the the LHsType
280−-}
281−
282−data Shielded a = SH Scope a -- Ignores its TScope, uses its own scope instead
283−
284−type family ProtectedSig a where
285− ProtectedSig GhcRn = HsWildCardBndrs GhcRn (HsImplicitBndrs
286− GhcRn
287− (Shielded (LHsType GhcRn)))
288− ProtectedSig GhcTc = NoExt
289−
290−class ProtectSig a where
291− protectSig :: Scope -> LHsSigWcType (NoGhcTc a) -> ProtectedSig a
292−
293−instance (HasLoc a) => HasLoc (Shielded a) where
294− loc (SH _ a) = loc a
295−
296−instance (ToHie (TScoped a)) => ToHie (TScoped (Shielded a)) where
297− toHie (TS _ (SH sc a)) = toHie (TS (ResolvedScopes [sc]) a)
298−
299−instance ProtectSig GhcTc where
300− protectSig _ _ = NoExt
301−
302−instance ProtectSig GhcRn where
303− protectSig sc (HsWC a (HsIB b sig)) =
304− HsWC a (HsIB b (SH sc sig))
305− protectSig _ _ = error "protectSig not given HsWC (HsIB)"
306−
307−class HasLoc a where
308− -- ^ defined so that HsImplicitBndrs and HsWildCardBndrs can
309− -- know what their implicit bindings are scoping over
310− loc :: a -> SrcSpan
311−
312−instance HasLoc thing => HasLoc (TScoped thing) where
313− loc (TS _ a) = loc a
314−
315−instance HasLoc thing => HasLoc (PScoped thing) where
316− loc (PS _ _ _ a) = loc a
317−
318−instance HasLoc (LHsQTyVars GhcRn) where
319− loc (HsQTvs _ vs) = loc vs
320− loc _ = noSrcSpan
321−
322−instance HasLoc thing => HasLoc (HsImplicitBndrs a thing) where
323− loc (HsIB _ a) = loc a
324− loc _ = noSrcSpan
325−
326−instance HasLoc thing => HasLoc (HsWildCardBndrs a thing) where
327− loc (HsWC _ a) = loc a
328− loc _ = noSrcSpan
329−
330−instance HasLoc (Located a) where
331− loc (L l _) = l
332−
333−instance HasLoc a => HasLoc [a] where
334− loc [] = noSrcSpan
335− loc xs = foldl1' combineSrcSpans $ map loc xs
336−
337−instance (HasLoc a, HasLoc b) => HasLoc (FamEqn s a b) where
338− loc (FamEqn _ a Nothing b _ c) = foldl1' combineSrcSpans [loc a, loc b, loc c]
339− loc (FamEqn _ a (Just tvs) b _ c) = foldl1' combineSrcSpans
340− [loc a, loc tvs, loc b, loc c]
341− loc _ = noSrcSpan
342−instance (HasLoc tm, HasLoc ty) => HasLoc (HsArg tm ty) where
343− loc (HsValArg tm) = loc tm
344− loc (HsTypeArg _ ty) = loc ty
345− loc (HsArgPar sp) = sp
346−
347−instance HasLoc (HsDataDefn GhcRn) where
348− loc def@(HsDataDefn{}) = loc $ dd_cons def
349− -- Only used for data family instances, so we only need rhs
350− -- Most probably the rest will be unhelpful anyway
351− loc _ = noSrcSpan
352−
353−instance HasLoc (Pat (GhcPass a)) where
354− loc (dL -> L l _) = l
355−
356−-- | The main worker class
357−class ToHie a where
358− toHie :: a -> HieM [HieAST Type]
359−
360−-- | Used to collect type info
361−class Data a => HasType a where
362− getTypeNode :: a -> HieM [HieAST Type]
363−
364−instance (ToHie a) => ToHie [a] where
365− toHie = concatMapM toHie
366−
367−instance (ToHie a) => ToHie (Bag a) where
368− toHie = toHie . bagToList
369−
370−instance (ToHie a) => ToHie (Maybe a) where
371− toHie = maybe (pure []) toHie
372−
373−instance ToHie (Context (Located NoExt)) where
374− toHie _ = pure []
375−
376−instance ToHie (TScoped NoExt) where
377− toHie _ = pure []
378−
379−instance ToHie (IEContext (Located ModuleName)) where
380− toHie (IEC c (L (RealSrcSpan span) mname)) =
381− pure $ [Node (NodeInfo S.empty [] idents) span []]
382− where details = mempty{identInfo = S.singleton (IEThing c)}
383− idents = M.singleton (Left mname) details
384− toHie _ = pure []
385−
386−instance ToHie (Context (Located Var)) where
387− toHie c = case c of
388− C context (L (RealSrcSpan span) name')
389− -> do
390− m <- asks name_remapping
391− let name = M.findWithDefault name' (varName name') m
392− pure
393− [Node
394− (NodeInfo S.empty [] $
395− M.singleton (Right $ varName name)
396− (IdentifierDetails (Just $ varType name')
397− (S.singleton context)))
398− span
399− []]
400− _ -> pure []
401−
402−instance ToHie (Context (Located Name)) where
403− toHie c = case c of
404− C context (L (RealSrcSpan span) name') -> do
405− m <- asks name_remapping
406− let name = case M.lookup name' m of
407− Just var -> varName var
408− Nothing -> name'
409− pure
410− [Node
411− (NodeInfo S.empty [] $
412− M.singleton (Right name)
413− (IdentifierDetails Nothing
414− (S.singleton context)))
415− span
416− []]
417− _ -> pure []
418−
419−-- | Dummy instances - never called
420−instance ToHie (TScoped (LHsSigWcType GhcTc)) where
421− toHie _ = pure []
422−instance ToHie (TScoped (LHsWcType GhcTc)) where
423− toHie _ = pure []
424−instance ToHie (SigContext (LSig GhcTc)) where
425− toHie _ = pure []
426−instance ToHie (TScoped Type) where
427− toHie _ = pure []
428−
429−instance HasType (LHsBind GhcRn) where
430− getTypeNode (L spn bind) = makeNode bind spn
431−
432−instance HasType (LHsBind GhcTc) where
433− getTypeNode (L spn bind) = case bind of
434− FunBind{fun_id = name} -> makeTypeNode bind spn (varType $ unLoc name)
435− _ -> makeNode bind spn
436−
437−instance HasType (LPat GhcRn) where
438− getTypeNode (dL -> L spn pat) = makeNode pat spn
439−
440−instance HasType (LPat GhcTc) where
441− getTypeNode (dL -> L spn opat) = makeTypeNode opat spn (hsPatType opat)
442−
443−instance HasType (LHsExpr GhcRn) where
444− getTypeNode (L spn e) = makeNode e spn
445−
446−-- | This instance tries to construct 'HieAST' nodes which include the type of
447−-- the expression. It is not yet possible to do this efficiently for all
448−-- expression forms, so we skip filling in the type for those inputs.
449−--
450−-- 'HsApp', for example, doesn't have any type information available directly on
451−-- the node. Our next recourse would be to desugar it into a 'CoreExpr' then
452−-- query the type of that. Yet both the desugaring call and the type query both
453−-- involve recursive calls to the function and argument! This is particularly
454−-- problematic when you realize that the HIE traversal will eventually visit
455−-- those nodes too and ask for their types again.
456−--
457−-- Since the above is quite costly, we just skip cases where computing the
458−-- expression's type is going to be expensive.
459−--
460−-- See #16233
461−instance HasType (LHsExpr GhcTc) where
462− getTypeNode e@(L spn e') = lift $
463− -- Some expression forms have their type immediately available
464− let tyOpt = case e' of
465− HsLit _ l -> Just (hsLitType l)
466− HsOverLit _ o -> Just (overLitType o)
467−
468− HsLam _ (MG { mg_ext = groupTy }) -> Just (matchGroupType groupTy)
469− HsLamCase _ (MG { mg_ext = groupTy }) -> Just (matchGroupType groupTy)
470− HsCase _ _ (MG { mg_ext = groupTy }) -> Just (mg_res_ty groupTy)
471−
472− ExplicitList ty _ _ -> Just (mkListTy ty)
473− ExplicitSum ty _ _ _ -> Just (mkSumTy ty)
474− HsDo ty _ _ -> Just ty
475− HsMultiIf ty _ -> Just ty
476−
477− _ -> Nothing
478−
479− in
480− case tyOpt of
481− _ | skipDesugaring e' -> fallback
482− | otherwise -> do
483− hs_env <- Hsc $ \e w -> return (e,w)
484− (_,mbe) <- liftIO $ deSugarExpr hs_env e
485− maybe fallback (makeTypeNode e' spn . exprType) mbe
486− where
487− fallback = makeNode e' spn
488−
489− matchGroupType :: MatchGroupTc -> Type
490− matchGroupType (MatchGroupTc args res) = mkFunTys args res
491−
492− -- | Skip desugaring of these expressions for performance reasons.
493− --
494− -- See impact on Haddock output (esp. missing type annotations or links)
495− -- before marking more things here as 'False'. See impact on Haddock
496− -- performance before marking more things as 'True'.
497− skipDesugaring :: HsExpr a -> Bool
498− skipDesugaring e = case e of
499− HsVar{} -> False
500− HsUnboundVar{} -> False
501− HsConLikeOut{} -> False
502− HsRecFld{} -> False
503− HsOverLabel{} -> False
504− HsIPVar{} -> False
505− HsWrap{} -> False
506− _ -> True
507−
508−instance ( ToHie (Context (Located (IdP a)))
509− , ToHie (MatchGroup a (LHsExpr a))
510− , ToHie (PScoped (LPat a))
511− , ToHie (GRHSs a (LHsExpr a))
512− , ToHie (LHsExpr a)
513− , ToHie (Located (PatSynBind a a))
514− , HasType (LHsBind a)
515− , ModifyState (IdP a)
516− , Data (HsBind a)
517− ) => ToHie (BindContext (LHsBind a)) where
518− toHie (BC context scope b@(L span bind)) =
519− concatM $ getTypeNode b : case bind of
520− FunBind{fun_id = name, fun_matches = matches} ->
521− [ toHie $ C (ValBind context scope $ getRealSpan span) name
522− , toHie matches
523− ]
524− PatBind{pat_lhs = lhs, pat_rhs = rhs} ->
525− [ toHie $ PS (getRealSpan span) scope NoScope lhs
526− , toHie rhs
527− ]
528− VarBind{var_rhs = expr} ->
529− [ toHie expr
530− ]
531− AbsBinds{abs_exports = xs, abs_binds = binds} ->
532− [ local (modifyState xs) $ -- Note [Name Remapping]
533− toHie $ fmap (BC context scope) binds
534− ]
535− PatSynBind _ psb ->
536− [ toHie $ L span psb -- PatSynBinds only occur at the top level
537− ]
538− XHsBindsLR _ -> []
539−
540−instance ( ToHie (LMatch a body)
541− ) => ToHie (MatchGroup a body) where
542− toHie mg = concatM $ case mg of
543− MG{ mg_alts = (L span alts) , mg_origin = FromSource } ->
544− [ pure $ locOnly span
545− , toHie alts
546− ]
547− MG{} -> []
548− XMatchGroup _ -> []
549−
550−instance ( ToHie (Context (Located (IdP a)))
551− , ToHie (PScoped (LPat a))
552− , ToHie (HsPatSynDir a)
553− ) => ToHie (Located (PatSynBind a a)) where
554− toHie (L sp psb) = concatM $ case psb of
555− PSB{psb_id=var, psb_args=dets, psb_def=pat, psb_dir=dir} ->
556− [ toHie $ C (Decl PatSynDec $ getRealSpan sp) var
557− , toHie $ toBind dets
558− , toHie $ PS Nothing lhsScope NoScope pat
559− , toHie dir
560− ]
561− where
562− lhsScope = combineScopes varScope detScope
563− varScope = mkLScope var
564− detScope = case dets of
565− (PrefixCon args) -> foldr combineScopes NoScope $ map mkLScope args
566− (InfixCon a b) -> combineScopes (mkLScope a) (mkLScope b)
567− (RecCon r) -> foldr go NoScope r
568− go (RecordPatSynField a b) c = combineScopes c
569− $ combineScopes (mkLScope a) (mkLScope b)
570− detSpan = case detScope of
571− LocalScope a -> Just a
572− _ -> Nothing
573− toBind (PrefixCon args) = PrefixCon $ map (C Use) args
574− toBind (InfixCon a b) = InfixCon (C Use a) (C Use b)
575− toBind (RecCon r) = RecCon $ map (PSC detSpan) r
576− XPatSynBind _ -> []
577−
578−instance ( ToHie (MatchGroup a (LHsExpr a))
579− ) => ToHie (HsPatSynDir a) where
580− toHie dir = case dir of
581− ExplicitBidirectional mg -> toHie mg
582− _ -> pure []
583−
584−instance ( a ~ GhcPass p
585− , ToHie body
586− , ToHie (HsMatchContext (NameOrRdrName (IdP a)))
587− , ToHie (PScoped (LPat a))
588− , ToHie (GRHSs a body)
589− , Data (Match a body)
590− ) => ToHie (LMatch (GhcPass p) body) where
591− toHie (L span m ) = concatM $ makeNode m span : case m of
592− Match{m_ctxt=mctx, m_pats = pats, m_grhss = grhss } ->
593− [ toHie mctx
594− , let rhsScope = mkScope $ grhss_span grhss
595− in toHie $ patScopes Nothing rhsScope NoScope pats
596− , toHie grhss
597− ]
598− XMatch _ -> []
599−
600−instance ( ToHie (Context (Located a))
601− ) => ToHie (HsMatchContext a) where
602− toHie (FunRhs{mc_fun=name}) = toHie $ C MatchBind name
603− toHie (StmtCtxt a) = toHie a
604− toHie _ = pure []
605−
606−instance ( ToHie (HsMatchContext a)
607− ) => ToHie (HsStmtContext a) where
608− toHie (PatGuard a) = toHie a
609− toHie (ParStmtCtxt a) = toHie a
610− toHie (TransStmtCtxt a) = toHie a
611− toHie _ = pure []
612−
613−instance ( a ~ GhcPass p
614− , ToHie (Context (Located (IdP a)))
615− , ToHie (RContext (HsRecFields a (PScoped (LPat a))))
616− , ToHie (LHsExpr a)
617− , ToHie (TScoped (LHsSigWcType a))
618− , ProtectSig a
619− , ToHie (TScoped (ProtectedSig a))
620− , HasType (LPat a)
621− , Data (HsSplice a)
622− ) => ToHie (PScoped (LPat (GhcPass p))) where
623− toHie (PS rsp scope pscope lpat@(dL -> L ospan opat)) =
624− concatM $ getTypeNode lpat : case opat of
625− WildPat _ ->
626− []
627− VarPat _ lname ->
628− [ toHie $ C (PatternBind scope pscope rsp) lname
629− ]
630− LazyPat _ p ->
631− [ toHie $ PS rsp scope pscope p
632− ]
633− AsPat _ lname pat ->
634− [ toHie $ C (PatternBind scope
635− (combineScopes (mkLScope (dL pat)) pscope)
636− rsp)
637− lname
638− , toHie $ PS rsp scope pscope pat
639− ]
640− ParPat _ pat ->
641− [ toHie $ PS rsp scope pscope pat
642− ]
643− BangPat _ pat ->
644− [ toHie $ PS rsp scope pscope pat
645− ]
646− ListPat _ pats ->
647− [ toHie $ patScopes rsp scope pscope pats
648− ]
649− TuplePat _ pats _ ->
650− [ toHie $ patScopes rsp scope pscope pats
651− ]
652− SumPat _ pat _ _ ->
653− [ toHie $ PS rsp scope pscope pat
654− ]
655− ConPatIn c dets ->
656− [ toHie $ C Use c
657− , toHie $ contextify dets
658− ]
659− ConPatOut {pat_con = con, pat_args = dets}->
660− [ toHie $ C Use $ fmap conLikeName con
661− , toHie $ contextify dets
662− ]
663− ViewPat _ expr pat ->
664− [ toHie expr
665− , toHie $ PS rsp scope pscope pat
666− ]
667− SplicePat _ sp ->
668− [ toHie $ L ospan sp
669− ]
670− LitPat _ _ ->
671− []
672− NPat _ _ _ _ ->
673− []
674− NPlusKPat _ n _ _ _ _ ->
675− [ toHie $ C (PatternBind scope pscope rsp) n
676− ]
677− SigPat _ pat sig ->
678− [ toHie $ PS rsp scope pscope pat
679− , let cscope = mkLScope (dL pat) in
680− toHie $ TS (ResolvedScopes [cscope, scope, pscope])
681− (protectSig @a cscope sig)
682− -- See Note [Scoping Rules for SigPat]
683− ]
684− CoPat _ _ _ _ ->
685− []
686− XPat _ -> []
687− where
688− contextify (PrefixCon args) = PrefixCon $ patScopes rsp scope pscope args
689− contextify (InfixCon a b) = InfixCon a' b'
690− where [a', b'] = patScopes rsp scope pscope [a,b]
691− contextify (RecCon r) = RecCon $ RC RecFieldMatch $ contextify_rec r
692− contextify_rec (HsRecFields fds a) = HsRecFields (map go scoped_fds) a
693− where
694− go (RS fscope (L spn (HsRecField lbl pat pun))) =
695− L spn $ HsRecField lbl (PS rsp scope fscope pat) pun
696− scoped_fds = listScopes pscope fds
697−
698−instance ( ToHie body
699− , ToHie (LGRHS a body)
700− , ToHie (RScoped (LHsLocalBinds a))
701− ) => ToHie (GRHSs a body) where
702− toHie grhs = concatM $ case grhs of
703− GRHSs _ grhss binds ->
704− [ toHie grhss
705− , toHie $ RS (mkScope $ grhss_span grhs) binds
706− ]
707− XGRHSs _ -> []
708−
709−instance ( ToHie (Located body)
710− , ToHie (RScoped (GuardLStmt a))
711− , Data (GRHS a (Located body))
712− ) => ToHie (LGRHS a (Located body)) where
713− toHie (L span g) = concatM $ makeNode g span : case g of
714− GRHS _ guards body ->
715− [ toHie $ listScopes (mkLScope body) guards
716− , toHie body
717− ]
718− XGRHS _ -> []
719−
720−instance ( a ~ GhcPass p
721− , ToHie (Context (Located (IdP a)))
722− , HasType (LHsExpr a)
723− , ToHie (PScoped (LPat a))
724− , ToHie (MatchGroup a (LHsExpr a))
725− , ToHie (LGRHS a (LHsExpr a))
726− , ToHie (RContext (HsRecordBinds a))
727− , ToHie (RFContext (Located (AmbiguousFieldOcc a)))
728− , ToHie (ArithSeqInfo a)
729− , ToHie (LHsCmdTop a)
730− , ToHie (RScoped (GuardLStmt a))
731− , ToHie (RScoped (LHsLocalBinds a))
732− , ToHie (TScoped (LHsWcType (NoGhcTc a)))
733− , ToHie (TScoped (LHsSigWcType (NoGhcTc a)))
734− , Data (HsExpr a)
735− , Data (HsSplice a)
736− , Data (HsTupArg a)
737− , Data (AmbiguousFieldOcc a)
738− ) => ToHie (LHsExpr (GhcPass p)) where
739− toHie e@(L mspan oexpr) = concatM $ getTypeNode e : case oexpr of
740− HsVar _ (L _ var) ->
741− [ toHie $ C Use (L mspan var)
742− -- Patch up var location since typechecker removes it
743− ]
744− HsUnboundVar _ _ ->
745− []
746− HsConLikeOut _ con ->
747− [ toHie $ C Use $ L mspan $ conLikeName con
748− ]
749− HsRecFld _ fld ->
750− [ toHie $ RFC RecFieldOcc Nothing (L mspan fld)
751− ]
752− HsOverLabel _ _ _ -> []
753− HsIPVar _ _ -> []
754− HsOverLit _ _ -> []
755− HsLit _ _ -> []
756− HsLam _ mg ->
757− [ toHie mg
758− ]
759− HsLamCase _ mg ->
760− [ toHie mg
761− ]
762− HsApp _ a b ->
763− [ toHie a
764− , toHie b
765− ]
766− HsAppType _ expr sig ->
767− [ toHie expr
768− , toHie $ TS (ResolvedScopes []) sig
769− ]
770− OpApp _ a b c ->
771− [ toHie a
772− , toHie b
773− , toHie c
774− ]
775− NegApp _ a _ ->
776− [ toHie a
777− ]
778− HsPar _ a ->
779− [ toHie a
780− ]
781− SectionL _ a b ->
782− [ toHie a
783− , toHie b
784− ]
785− SectionR _ a b ->
786− [ toHie a
787− , toHie b
788− ]
789− ExplicitTuple _ args _ ->
790− [ toHie args
791− ]
792− ExplicitSum _ _ _ expr ->
793− [ toHie expr
794− ]
795− HsCase _ expr matches ->
796− [ toHie expr
797− , toHie matches
798− ]
799− HsIf _ _ a b c ->
800− [ toHie a
801− , toHie b
802− , toHie c
803− ]
804− HsMultiIf _ grhss ->
805− [ toHie grhss
806− ]
807− HsLet _ binds expr ->
808− [ toHie $ RS (mkLScope expr) binds
809− , toHie expr
810− ]
811− HsDo _ _ (L ispan stmts) ->
812− [ pure $ locOnly ispan
813− , toHie $ listScopes NoScope stmts
814− ]
815− ExplicitList _ _ exprs ->
816− [ toHie exprs
817− ]
818− RecordCon {rcon_con_name = name, rcon_flds = binds}->
819− [ toHie $ C Use name
820− , toHie $ RC RecFieldAssign $ binds
821− ]
822− RecordUpd {rupd_expr = expr, rupd_flds = upds}->
823− [ toHie expr
824− , toHie $ map (RC RecFieldAssign) upds
825− ]
826− ExprWithTySig _ expr sig ->
827− [ toHie expr
828− , toHie $ TS (ResolvedScopes [mkLScope expr]) sig
829− ]
830− ArithSeq _ _ info ->
831− [ toHie info
832− ]
833− HsSCC _ _ _ expr ->
834− [ toHie expr
835− ]
836− HsCoreAnn _ _ _ expr ->
837− [ toHie expr
838− ]
839− HsProc _ pat cmdtop ->
840− [ toHie $ PS Nothing (mkLScope cmdtop) NoScope pat
841− , toHie cmdtop
842− ]
843− HsStatic _ expr ->
844− [ toHie expr
845− ]
846− HsArrApp _ a b _ _ ->
847− [ toHie a
848− , toHie b
849− ]
850− HsArrForm _ expr _ cmds ->
851− [ toHie expr
852− , toHie cmds
853− ]
854− HsTick _ _ expr ->
855− [ toHie expr
856− ]
857− HsBinTick _ _ _ expr ->
858− [ toHie expr
859− ]
860− HsTickPragma _ _ _ _ expr ->
861− [ toHie expr
862− ]
863− HsWrap _ _ a ->
864− [ toHie $ L mspan a
865− ]
866− HsBracket _ b ->
867− [ toHie b
868− ]
869− HsRnBracketOut _ b p ->
870− [ toHie b
871− , toHie p
872− ]
873− HsTcBracketOut _ b p ->
874− [ toHie b
875− , toHie p
876− ]
877− HsSpliceE _ x ->
878− [ toHie $ L mspan x
879− ]
880− EWildPat _ -> []
881− EAsPat _ a b ->
882− [ toHie $ C Use a
883− , toHie b
884− ]
885− EViewPat _ a b ->
886− [ toHie a
887− , toHie b
888− ]
889− ELazyPat _ a ->
890− [ toHie a
891− ]
892− XExpr _ -> []
893−
894−instance ( a ~ GhcPass p
895− , ToHie (LHsExpr a)
896− , Data (HsTupArg a)
897− ) => ToHie (LHsTupArg (GhcPass p)) where
898− toHie (L span arg) = concatM $ makeNode arg span : case arg of
899− Present _ expr ->
900− [ toHie expr
901− ]
902− Missing _ -> []
903− XTupArg _ -> []
904−
905−instance ( a ~ GhcPass p
906− , ToHie (PScoped (LPat a))
907− , ToHie (LHsExpr a)
908− , ToHie (SigContext (LSig a))
909− , ToHie (RScoped (LHsLocalBinds a))
910− , ToHie (RScoped (ApplicativeArg a))
911− , ToHie (Located body)
912− , Data (StmtLR a a (Located body))
913− , Data (StmtLR a a (Located (HsExpr a)))
914− ) => ToHie (RScoped (LStmt (GhcPass p) (Located body))) where
915− toHie (RS scope (L span stmt)) = concatM $ makeNode stmt span : case stmt of
916− LastStmt _ body _ _ ->
917− [ toHie body
918− ]
919− BindStmt _ pat body _ _ ->
920− [ toHie $ PS (getRealSpan $ getLoc body) scope NoScope pat
921− , toHie body
922− ]
923− ApplicativeStmt _ stmts _ ->
924− [ concatMapM (toHie . RS scope . snd) stmts
925− ]
926− BodyStmt _ body _ _ ->
927− [ toHie body
928− ]
929− LetStmt _ binds ->
930− [ toHie $ RS scope binds
931− ]
932− ParStmt _ parstmts _ _ ->
933− [ concatMapM (\(ParStmtBlock _ stmts _ _) ->
934− toHie $ listScopes NoScope stmts)
935− parstmts
936− ]
937− TransStmt {trS_stmts = stmts, trS_using = using, trS_by = by} ->
938− [ toHie $ listScopes scope stmts
939− , toHie using
940− , toHie by
941− ]
942− RecStmt {recS_stmts = stmts} ->
943− [ toHie $ map (RS $ combineScopes scope (mkScope span)) stmts
944− ]
945− XStmtLR _ -> []
946−
947−instance ( ToHie (LHsExpr a)
948− , ToHie (PScoped (LPat a))
949− , ToHie (BindContext (LHsBind a))
950− , ToHie (SigContext (LSig a))
951− , ToHie (RScoped (HsValBindsLR a a))
952− , Data (HsLocalBinds a)
953− ) => ToHie (RScoped (LHsLocalBinds a)) where
954− toHie (RS scope (L sp binds)) = concatM $ makeNode binds sp : case binds of
955− EmptyLocalBinds _ -> []
956− HsIPBinds _ _ -> []
957− HsValBinds _ valBinds ->
958− [ toHie $ RS (combineScopes scope $ mkScope sp)
959− valBinds
960− ]
961− XHsLocalBindsLR _ -> []
962−
963−instance ( ToHie (BindContext (LHsBind a))
964− , ToHie (SigContext (LSig a))
965− , ToHie (RScoped (XXValBindsLR a a))
966− ) => ToHie (RScoped (HsValBindsLR a a)) where
967− toHie (RS sc v) = concatM $ case v of
968− ValBinds _ binds sigs ->
969− [ toHie $ fmap (BC RegularBind sc) binds
970− , toHie $ fmap (SC (SI BindSig Nothing)) sigs
971− ]
972− XValBindsLR x -> [ toHie $ RS sc x ]
973−
974−instance ToHie (RScoped (NHsValBindsLR GhcTc)) where
975− toHie (RS sc (NValBinds binds sigs)) = concatM $
976− [ toHie (concatMap (map (BC RegularBind sc) . bagToList . snd) binds)
977− , toHie $ fmap (SC (SI BindSig Nothing)) sigs
978− ]
979−instance ToHie (RScoped (NHsValBindsLR GhcRn)) where
980− toHie (RS sc (NValBinds binds sigs)) = concatM $
981− [ toHie (concatMap (map (BC RegularBind sc) . bagToList . snd) binds)
982− , toHie $ fmap (SC (SI BindSig Nothing)) sigs
983− ]
984−
985−instance ( ToHie (RContext (LHsRecField a arg))
986− ) => ToHie (RContext (HsRecFields a arg)) where
987− toHie (RC c (HsRecFields fields _)) = toHie $ map (RC c) fields
988−
989−instance ( ToHie (RFContext (Located label))
990− , ToHie arg
991− , HasLoc arg
992− , Data label
993− , Data arg
994− ) => ToHie (RContext (LHsRecField' label arg)) where
995− toHie (RC c (L span recfld)) = concatM $ makeNode recfld span : case recfld of
996− HsRecField label expr _ ->
997− [ toHie $ RFC c (getRealSpan $ loc expr) label
998− , toHie expr
999− ]
1000−
1001−removeDefSrcSpan :: Name -> Name
1002−removeDefSrcSpan n = setNameLoc n noSrcSpan
1003−
1004−instance ToHie (RFContext (LFieldOcc GhcRn)) where
1005− toHie (RFC c rhs (L nspan f)) = concatM $ case f of
1006− FieldOcc name _ ->
1007− [ toHie $ C (RecField c rhs) (L nspan $ removeDefSrcSpan name)
1008− ]
1009− XFieldOcc _ -> []
1010−
1011−instance ToHie (RFContext (LFieldOcc GhcTc)) where
1012− toHie (RFC c rhs (L nspan f)) = concatM $ case f of
1013− FieldOcc var _ ->
1014− let var' = setVarName var (removeDefSrcSpan $ varName var)
1015− in [ toHie $ C (RecField c rhs) (L nspan var')
1016− ]
1017− XFieldOcc _ -> []
1018−
1019−instance ToHie (RFContext (Located (AmbiguousFieldOcc GhcRn))) where
1020− toHie (RFC c rhs (L nspan afo)) = concatM $ case afo of
1021− Unambiguous name _ ->
1022− [ toHie $ C (RecField c rhs) $ L nspan $ removeDefSrcSpan name
1023− ]
1024− Ambiguous _name _ ->
1025− [ ]
1026− XAmbiguousFieldOcc _ -> []
1027−
1028−instance ToHie (RFContext (Located (AmbiguousFieldOcc GhcTc))) where
1029− toHie (RFC c rhs (L nspan afo)) = concatM $ case afo of
1030− Unambiguous var _ ->
1031− let var' = setVarName var (removeDefSrcSpan $ varName var)
1032− in [ toHie $ C (RecField c rhs) (L nspan var')
1033− ]
1034− Ambiguous var _ ->
1035− let var' = setVarName var (removeDefSrcSpan $ varName var)
1036− in [ toHie $ C (RecField c rhs) (L nspan var')
1037− ]
1038− XAmbiguousFieldOcc _ -> []
1039−
1040−instance ( a ~ GhcPass p
1041− , ToHie (PScoped (LPat a))
1042− , ToHie (BindContext (LHsBind a))
1043− , ToHie (LHsExpr a)
1044− , ToHie (SigContext (LSig a))
1045− , ToHie (RScoped (HsValBindsLR a a))
1046− , Data (StmtLR a a (Located (HsExpr a)))
1047− , Data (HsLocalBinds a)
1048− ) => ToHie (RScoped (ApplicativeArg (GhcPass p))) where
1049− toHie (RS sc (ApplicativeArgOne _ pat expr _)) = concatM
1050− [ toHie $ PS Nothing sc NoScope pat
1051− , toHie expr
1052− ]
1053− toHie (RS sc (ApplicativeArgMany _ stmts _ pat)) = concatM
1054− [ toHie $ listScopes NoScope stmts
1055− , toHie $ PS Nothing sc NoScope pat
1056− ]
1057− toHie (RS _ (XApplicativeArg _)) = pure []
1058−
1059−instance (ToHie arg, ToHie rec) => ToHie (HsConDetails arg rec) where
1060− toHie (PrefixCon args) = toHie args
1061− toHie (RecCon rec) = toHie rec
1062− toHie (InfixCon a b) = concatM [ toHie a, toHie b]
1063−
1064−instance ( ToHie (LHsCmd a)
1065− , Data (HsCmdTop a)
1066− ) => ToHie (LHsCmdTop a) where
1067− toHie (L span top) = concatM $ makeNode top span : case top of
1068− HsCmdTop _ cmd ->
1069− [ toHie cmd
1070− ]
1071− XCmdTop _ -> []
1072−
1073−instance ( a ~ GhcPass p
1074− , ToHie (PScoped (LPat a))
1075− , ToHie (BindContext (LHsBind a))
1076− , ToHie (LHsExpr a)
1077− , ToHie (MatchGroup a (LHsCmd a))
1078− , ToHie (SigContext (LSig a))
1079− , ToHie (RScoped (HsValBindsLR a a))
1080− , Data (HsCmd a)
1081− , Data (HsCmdTop a)
1082− , Data (StmtLR a a (Located (HsCmd a)))
1083− , Data (HsLocalBinds a)
1084− , Data (StmtLR a a (Located (HsExpr a)))
1085− ) => ToHie (LHsCmd (GhcPass p)) where
1086− toHie (L span cmd) = concatM $ makeNode cmd span : case cmd of
1087− HsCmdArrApp _ a b _ _ ->
1088− [ toHie a
1089− , toHie b
1090− ]
1091− HsCmdArrForm _ a _ _ cmdtops ->
1092− [ toHie a
1093− , toHie cmdtops
1094− ]
1095− HsCmdApp _ a b ->
1096− [ toHie a
1097− , toHie b
1098− ]
1099− HsCmdLam _ mg ->
1100− [ toHie mg
1101− ]
1102− HsCmdPar _ a ->
1103− [ toHie a
1104− ]
1105− HsCmdCase _ expr alts ->
1106− [ toHie expr
1107− , toHie alts
1108− ]
1109− HsCmdIf _ _ a b c ->
1110− [ toHie a
1111− , toHie b
1112− , toHie c
1113− ]
1114− HsCmdLet _ binds cmd' ->
1115− [ toHie $ RS (mkLScope cmd') binds
1116− , toHie cmd'
1117− ]
1118− HsCmdDo _ (L ispan stmts) ->
1119− [ pure $ locOnly ispan
1120− , toHie $ listScopes NoScope stmts
1121− ]
1122− HsCmdWrap _ _ _ -> []
1123− XCmd _ -> []
1124−
1125−instance ToHie (TyClGroup GhcRn) where
1126− toHie (TyClGroup _ classes roles instances) = concatM
1127− [ toHie classes
1128− , toHie roles
1129− , toHie instances
1130− ]
1131− toHie (XTyClGroup _) = pure []
1132−
1133−instance ToHie (LTyClDecl GhcRn) where
1134− toHie (L span decl) = concatM $ makeNode decl span : case decl of
1135− FamDecl {tcdFam = fdecl} ->
1136− [ toHie (L span fdecl)
1137− ]
1138− SynDecl {tcdLName = name, tcdTyVars = vars, tcdRhs = typ} ->
1139− [ toHie $ C (Decl SynDec $ getRealSpan span) name
1140− , toHie $ TS (ResolvedScopes [mkScope $ getLoc typ]) vars
1141− , toHie typ
1142− ]
1143− DataDecl {tcdLName = name, tcdTyVars = vars, tcdDataDefn = defn} ->
1144− [ toHie $ C (Decl DataDec $ getRealSpan span) name
1145− , toHie $ TS (ResolvedScopes [quant_scope, rhs_scope]) vars
1146− , toHie defn
1147− ]
1148− where
1149− quant_scope = mkLScope $ dd_ctxt defn
1150− rhs_scope = sig_sc `combineScopes` con_sc `combineScopes` deriv_sc
1151− sig_sc = maybe NoScope mkLScope $ dd_kindSig defn
1152− con_sc = foldr combineScopes NoScope $ map mkLScope $ dd_cons defn
1153− deriv_sc = mkLScope $ dd_derivs defn
1154− ClassDecl { tcdCtxt = context
1155− , tcdLName = name
1156− , tcdTyVars = vars
1157− , tcdFDs = deps
1158− , tcdSigs = sigs
1159− , tcdMeths = meths
1160− , tcdATs = typs
1161− , tcdATDefs = deftyps
1162− } ->
1163− [ toHie $ C (Decl ClassDec $ getRealSpan span) name
1164− , toHie context
1165− , toHie $ TS (ResolvedScopes [context_scope, rhs_scope]) vars
1166− , toHie deps
1167− , toHie $ map (SC $ SI ClassSig $ getRealSpan span) sigs
1168− , toHie $ fmap (BC InstanceBind ModuleScope) meths
1169− , toHie typs
1170− , concatMapM (pure . locOnly . getLoc) deftyps
1171− , toHie $ map (go . unLoc) deftyps
1172− ]
1173− where
1174− context_scope = mkLScope context
1175− rhs_scope = foldl1' combineScopes $ map mkScope
1176− [ loc deps, loc sigs, loc (bagToList meths), loc typs, loc deftyps]
1177−
1178− go :: TyFamDefltEqn GhcRn
1179− -> FamEqn GhcRn (TScoped (LHsQTyVars GhcRn)) (LHsType GhcRn)
1180− go (FamEqn a var bndrs pat b rhs) =
1181− FamEqn a var bndrs (TS (ResolvedScopes [mkLScope rhs]) pat) b rhs
1182− go (XFamEqn NoExt) = XFamEqn NoExt
1183− XTyClDecl _ -> []
1184−
1185−instance ToHie (LFamilyDecl GhcRn) where
1186− toHie (L span decl) = concatM $ makeNode decl span : case decl of
1187− FamilyDecl _ info name vars _ sig inj ->
1188− [ toHie $ C (Decl FamDec $ getRealSpan span) name
1189− , toHie $ TS (ResolvedScopes [rhsSpan]) vars
1190− , toHie info
1191− , toHie $ RS injSpan sig
1192− , toHie inj
1193− ]
1194− where
1195− rhsSpan = sigSpan `combineScopes` injSpan
1196− sigSpan = mkScope $ getLoc sig
1197− injSpan = maybe NoScope (mkScope . getLoc) inj
1198− XFamilyDecl _ -> []
1199−
1200−instance ToHie (FamilyInfo GhcRn) where
1201− toHie (ClosedTypeFamily (Just eqns)) = concatM $
1202− [ concatMapM (pure . locOnly . getLoc) eqns
1203− , toHie $ map go eqns
1204− ]
1205− where
1206− go (L l ib) = TS (ResolvedScopes [mkScope l]) ib
1207− toHie _ = pure []
1208−
1209−instance ToHie (RScoped (LFamilyResultSig GhcRn)) where
1210− toHie (RS sc (L span sig)) = concatM $ makeNode sig span : case sig of
1211− NoSig _ ->
1212− []
1213− KindSig _ k ->
1214− [ toHie k
1215− ]
1216− TyVarSig _ bndr ->
1217− [ toHie $ TVS (ResolvedScopes [sc]) NoScope bndr
1218− ]
1219− XFamilyResultSig _ -> []
1220−
1221−instance ToHie (Located (FunDep (Located Name))) where
1222− toHie (L span fd@(lhs, rhs)) = concatM $
1223− [ makeNode fd span
1224− , toHie $ map (C Use) lhs
1225− , toHie $ map (C Use) rhs
1226− ]
1227−
1228−instance (ToHie pats, ToHie rhs, HasLoc pats, HasLoc rhs)
1229− => ToHie (TScoped (FamEqn GhcRn pats rhs)) where
1230− toHie (TS _ f) = toHie f
1231−
1232−instance ( ToHie pats
1233− , ToHie rhs
1234− , HasLoc pats
1235− , HasLoc rhs
1236− ) => ToHie (FamEqn GhcRn pats rhs) where
1237− toHie fe@(FamEqn _ var tybndrs pats _ rhs) = concatM $
1238− [ toHie $ C (Decl InstDec $ getRealSpan $ loc fe) var
1239− , toHie $ fmap (tvScopes (ResolvedScopes []) scope) tybndrs
1240− , toHie pats
1241− , toHie rhs
1242− ]
1243− where scope = combineScopes patsScope rhsScope
1244− patsScope = mkScope (loc pats)
1245− rhsScope = mkScope (loc rhs)
1246− toHie (XFamEqn _) = pure []
1247−
1248−instance ToHie (LInjectivityAnn GhcRn) where
1249− toHie (L span ann) = concatM $ makeNode ann span : case ann of
1250− InjectivityAnn lhs rhs ->
1251− [ toHie $ C Use lhs
1252− , toHie $ map (C Use) rhs
1253− ]
1254−
1255−instance ToHie (HsDataDefn GhcRn) where
1256− toHie (HsDataDefn _ _ ctx _ mkind cons derivs) = concatM
1257− [ toHie ctx
1258− , toHie mkind
1259− , toHie cons
1260− , toHie derivs
1261− ]
1262− toHie (XHsDataDefn _) = pure []
1263−
1264−instance ToHie (HsDeriving GhcRn) where
1265− toHie (L span clauses) = concatM
1266− [ pure $ locOnly span
1267− , toHie clauses
1268− ]
1269−
1270−instance ToHie (LHsDerivingClause GhcRn) where
1271− toHie (L span cl) = concatM $ makeNode cl span : case cl of
1272− HsDerivingClause _ strat (L ispan tys) ->
1273− [ toHie strat
1274− , pure $ locOnly ispan
1275− , toHie $ map (TS (ResolvedScopes [])) tys
1276− ]
1277− XHsDerivingClause _ -> []
1278−
1279−instance ToHie (Located (DerivStrategy GhcRn)) where
1280− toHie (L span strat) = concatM $ makeNode strat span : case strat of
1281− StockStrategy -> []
1282− AnyclassStrategy -> []
1283− NewtypeStrategy -> []
1284− ViaStrategy s -> [ toHie $ TS (ResolvedScopes []) s ]
1285−
1286−instance ToHie (Located OverlapMode) where
1287− toHie (L span _) = pure $ locOnly span
1288−
1289−instance ToHie (LConDecl GhcRn) where
1290− toHie (L span decl) = concatM $ makeNode decl span : case decl of
1291− ConDeclGADT { con_names = names, con_qvars = qvars
1292− , con_mb_cxt = ctx, con_args = args, con_res_ty = typ } ->
1293− [ toHie $ map (C (Decl ConDec $ getRealSpan span)) names
1294− , toHie $ TS (ResolvedScopes [ctxScope, rhsScope]) qvars
1295− , toHie ctx
1296− , toHie args
1297− , toHie typ
1298− ]
1299− where
1300− rhsScope = combineScopes argsScope tyScope
1301− ctxScope = maybe NoScope mkLScope ctx
1302− argsScope = condecl_scope args
1303− tyScope = mkLScope typ
1304− ConDeclH98 { con_name = name, con_ex_tvs = qvars
1305− , con_mb_cxt = ctx, con_args = dets } ->
1306− [ toHie $ C (Decl ConDec $ getRealSpan span) name
1307− , toHie $ tvScopes (ResolvedScopes []) rhsScope qvars
1308− , toHie ctx
1309− , toHie dets
1310− ]
1311− where
1312− rhsScope = combineScopes ctxScope argsScope
1313− ctxScope = maybe NoScope mkLScope ctx
1314− argsScope = condecl_scope dets
1315− XConDecl _ -> []
1316− where condecl_scope args = case args of
1317− PrefixCon xs -> foldr combineScopes NoScope $ map mkLScope xs
1318− InfixCon a b -> combineScopes (mkLScope a) (mkLScope b)
1319− RecCon x -> mkLScope x
1320−
1321−instance ToHie (Located [LConDeclField GhcRn]) where
1322− toHie (L span decls) = concatM $
1323− [ pure $ locOnly span
1324− , toHie decls
1325− ]
1326−
1327−instance ( HasLoc thing
1328− , ToHie (TScoped thing)
1329− ) => ToHie (TScoped (HsImplicitBndrs GhcRn thing)) where
1330− toHie (TS sc (HsIB ibrn a)) = concatM $
1331− [ pure $ bindingsOnly $ map (C $ TyVarBind (mkScope span) sc) ibrn
1332− , toHie $ TS sc a
1333− ]
1334− where span = loc a
1335− toHie (TS _ (XHsImplicitBndrs _)) = pure []
1336−
1337−instance ( HasLoc thing
1338− , ToHie (TScoped thing)
1339− ) => ToHie (TScoped (HsWildCardBndrs GhcRn thing)) where
1340− toHie (TS sc (HsWC names a)) = concatM $
1341− [ pure $ bindingsOnly $ map (C $ TyVarBind (mkScope span) sc) names
1342− , toHie $ TS sc a
1343− ]
1344− where span = loc a
1345− toHie (TS _ (XHsWildCardBndrs _)) = pure []
1346−
1347−instance ToHie (SigContext (LSig GhcRn)) where
1348− toHie (SC (SI styp msp) (L sp sig)) = concatM $ makeNode sig sp : case sig of
1349− TypeSig _ names typ ->
1350− [ toHie $ map (C TyDecl) names
1351− , toHie $ TS (UnresolvedScope (map unLoc names) Nothing) typ
1352− ]
1353− PatSynSig _ names typ ->
1354− [ toHie $ map (C TyDecl) names
1355− , toHie $ TS (UnresolvedScope (map unLoc names) Nothing) typ
1356− ]
1357− ClassOpSig _ _ names typ ->
1358− [ case styp of
1359− ClassSig -> toHie $ map (C $ ClassTyDecl $ getRealSpan sp) names
1360− _ -> toHie $ map (C $ TyDecl) names
1361− , toHie $ TS (UnresolvedScope (map unLoc names) msp) typ
1362− ]
1363− IdSig _ _ -> []
1364− FixSig _ fsig ->
1365− [ toHie $ L sp fsig
1366− ]
1367− InlineSig _ name _ ->
1368− [ toHie $ (C Use) name
1369− ]
1370− SpecSig _ name typs _ ->
1371− [ toHie $ (C Use) name
1372− , toHie $ map (TS (ResolvedScopes [])) typs
1373− ]
1374− SpecInstSig _ _ typ ->
1375− [ toHie $ TS (ResolvedScopes []) typ
1376− ]
1377− MinimalSig _ _ form ->
1378− [ toHie form
1379− ]
1380− SCCFunSig _ _ name mtxt ->
1381− [ toHie $ (C Use) name
1382− , pure $ maybe [] (locOnly . getLoc) mtxt
1383− ]
1384− CompleteMatchSig _ _ (L ispan names) typ ->
1385− [ pure $ locOnly ispan
1386− , toHie $ map (C Use) names
1387− , toHie $ fmap (C Use) typ
1388− ]
1389− XSig _ -> []
1390−
1391−instance ToHie (LHsType GhcRn) where
1392− toHie x = toHie $ TS (ResolvedScopes []) x
1393−
1394−instance ToHie (TScoped (LHsType GhcRn)) where
1395− toHie (TS tsc (L span t)) = concatM $ makeNode t span : case t of
1396− HsForAllTy _ bndrs body ->
1397− [ toHie $ tvScopes tsc (mkScope $ getLoc body) bndrs
1398− , toHie body
1399− ]
1400− HsQualTy _ ctx body ->
1401− [ toHie ctx
1402− , toHie body
1403− ]
1404− HsTyVar _ _ var ->
1405− [ toHie $ C Use var
1406− ]
1407− HsAppTy _ a b ->
1408− [ toHie a
1409− , toHie b
1410− ]
1411− HsAppKindTy _ ty ki ->
1412− [ toHie ty
1413− , toHie $ TS (ResolvedScopes []) ki
1414− ]
1415− HsFunTy _ a b ->
1416− [ toHie a
1417− , toHie b
1418− ]
1419− HsListTy _ a ->
1420− [ toHie a
1421− ]
1422− HsTupleTy _ _ tys ->
1423− [ toHie tys
1424− ]
1425− HsSumTy _ tys ->
1426− [ toHie tys
1427− ]
1428− HsOpTy _ a op b ->
1429− [ toHie a
1430− , toHie $ C Use op
1431− , toHie b
1432− ]
1433− HsParTy _ a ->
1434− [ toHie a
1435− ]
1436− HsIParamTy _ ip ty ->
1437− [ toHie ip
1438− , toHie ty
1439− ]
1440− HsKindSig _ a b ->
1441− [ toHie a
1442− , toHie b
1443− ]
1444− HsSpliceTy _ a ->
1445− [ toHie $ L span a
1446− ]
1447− HsDocTy _ a _ ->
1448− [ toHie a
1449− ]
1450− HsBangTy _ _ ty ->
1451− [ toHie ty
1452− ]
1453− HsRecTy _ fields ->
1454− [ toHie fields
1455− ]
1456− HsExplicitListTy _ _ tys ->
1457− [ toHie tys
1458− ]
1459− HsExplicitTupleTy _ tys ->
1460− [ toHie tys
1461− ]
1462− HsTyLit _ _ -> []
1463− HsWildCardTy _ -> []
1464− HsStarTy _ _ -> []
1465− XHsType _ -> []
1466−
1467−instance (ToHie tm, ToHie ty) => ToHie (HsArg tm ty) where
1468− toHie (HsValArg tm) = toHie tm
1469− toHie (HsTypeArg _ ty) = toHie ty
1470− toHie (HsArgPar sp) = pure $ locOnly sp
1471−
1472−instance ToHie (TVScoped (LHsTyVarBndr GhcRn)) where
1473− toHie (TVS tsc sc (L span bndr)) = concatM $ makeNode bndr span : case bndr of
1474− UserTyVar _ var ->
1475− [ toHie $ C (TyVarBind sc tsc) var
1476− ]
1477− KindedTyVar _ var kind ->
1478− [ toHie $ C (TyVarBind sc tsc) var
1479− , toHie kind
1480− ]
1481− XTyVarBndr _ -> []
1482−
1483−instance ToHie (TScoped (LHsQTyVars GhcRn)) where
1484− toHie (TS sc (HsQTvs (HsQTvsRn implicits _) vars)) = concatM $
1485− [ pure $ bindingsOnly bindings
1486− , toHie $ tvScopes sc NoScope vars
1487− ]
1488− where
1489− varLoc = loc vars
1490− bindings = map (C $ TyVarBind (mkScope varLoc) sc) implicits
1491− toHie (TS _ (XLHsQTyVars _)) = pure []
1492−
1493−instance ToHie (LHsContext GhcRn) where
1494− toHie (L span tys) = concatM $
1495− [ pure $ locOnly span
1496− , toHie tys
1497− ]
1498−
1499−instance ToHie (LConDeclField GhcRn) where
1500− toHie (L span field) = concatM $ makeNode field span : case field of
1501− ConDeclField _ fields typ _ ->
1502− [ toHie $ map (RFC RecFieldDecl (getRealSpan $ loc typ)) fields
1503− , toHie typ
1504− ]
1505− XConDeclField _ -> []
1506−
1507−instance ToHie (LHsExpr a) => ToHie (ArithSeqInfo a) where
1508− toHie (From expr) = toHie expr
1509− toHie (FromThen a b) = concatM $
1510− [ toHie a
1511− , toHie b
1512− ]
1513− toHie (FromTo a b) = concatM $
1514− [ toHie a
1515− , toHie b
1516− ]
1517− toHie (FromThenTo a b c) = concatM $
1518− [ toHie a
1519− , toHie b
1520− , toHie c
1521− ]
1522−
1523−instance ToHie (LSpliceDecl GhcRn) where
1524− toHie (L span decl) = concatM $ makeNode decl span : case decl of
1525− SpliceDecl _ splice _ ->
1526− [ toHie splice
1527− ]
1528− XSpliceDecl _ -> []
1529−
1530−instance ToHie (HsBracket a) where
1531− toHie _ = pure []
1532−
1533−instance ToHie PendingRnSplice where
1534− toHie _ = pure []
1535−
1536−instance ToHie PendingTcSplice where
1537− toHie _ = pure []
1538−
1539−instance ToHie (LBooleanFormula (Located Name)) where
1540− toHie (L span form) = concatM $ makeNode form span : case form of
1541− Var a ->
1542− [ toHie $ C Use a
1543− ]
1544− And forms ->
1545− [ toHie forms
1546− ]
1547− Or forms ->
1548− [ toHie forms
1549− ]
1550− Parens f ->
1551− [ toHie f
1552− ]
1553−
1554−instance ToHie (Located HsIPName) where
1555− toHie (L span e) = makeNode e span
1556−
1557−instance ( ToHie (LHsExpr a)
1558− , Data (HsSplice a)
1559− ) => ToHie (Located (HsSplice a)) where
1560− toHie (L span sp) = concatM $ makeNode sp span : case sp of
1561− HsTypedSplice _ _ _ expr ->
1562− [ toHie expr
1563− ]
1564− HsUntypedSplice _ _ _ expr ->
1565− [ toHie expr
1566− ]
1567− HsQuasiQuote _ _ _ ispan _ ->
1568− [ pure $ locOnly ispan
1569− ]
1570− HsSpliced _ _ _ ->
1571− []
1572− HsSplicedT _ ->
1573− []
1574− XSplice _ -> []
1575−
1576−instance ToHie (LRoleAnnotDecl GhcRn) where
1577− toHie (L span annot) = concatM $ makeNode annot span : case annot of
1578− RoleAnnotDecl _ var roles ->
1579− [ toHie $ C Use var
1580− , concatMapM (pure . locOnly . getLoc) roles
1581− ]
1582− XRoleAnnotDecl _ -> []
1583−
1584−instance ToHie (LInstDecl GhcRn) where
1585− toHie (L span decl) = concatM $ makeNode decl span : case decl of
1586− ClsInstD _ d ->
1587− [ toHie $ L span d
1588− ]
1589− DataFamInstD _ d ->
1590− [ toHie $ L span d
1591− ]
1592− TyFamInstD _ d ->
1593− [ toHie $ L span d
1594− ]
1595− XInstDecl _ -> []
1596−
1597−instance ToHie (LClsInstDecl GhcRn) where
1598− toHie (L span decl) = concatM
1599− [ toHie $ TS (ResolvedScopes [mkScope span]) $ cid_poly_ty decl
1600− , toHie $ fmap (BC InstanceBind ModuleScope) $ cid_binds decl
1601− , toHie $ map (SC $ SI InstSig $ getRealSpan span) $ cid_sigs decl
1602− , pure $ concatMap (locOnly . getLoc) $ cid_tyfam_insts decl
1603− , toHie $ cid_tyfam_insts decl
1604− , pure $ concatMap (locOnly . getLoc) $ cid_datafam_insts decl
1605− , toHie $ cid_datafam_insts decl
1606− , toHie $ cid_overlap_mode decl
1607− ]
1608−
1609−instance ToHie (LDataFamInstDecl GhcRn) where
1610− toHie (L sp (DataFamInstDecl d)) = toHie $ TS (ResolvedScopes [mkScope sp]) d
1611−
1612−instance ToHie (LTyFamInstDecl GhcRn) where
1613− toHie (L sp (TyFamInstDecl d)) = toHie $ TS (ResolvedScopes [mkScope sp]) d
1614−
1615−instance ToHie (Context a)
1616− => ToHie (PatSynFieldContext (RecordPatSynField a)) where
1617− toHie (PSC sp (RecordPatSynField a b)) = concatM $
1618− [ toHie $ C (RecField RecFieldDecl sp) a
1619− , toHie $ C Use b
1620− ]
1621−
1622−instance ToHie (LDerivDecl GhcRn) where
1623− toHie (L span decl) = concatM $ makeNode decl span : case decl of
1624− DerivDecl _ typ strat overlap ->
1625− [ toHie $ TS (ResolvedScopes []) typ
1626− , toHie strat
1627− , toHie overlap
1628− ]
1629− XDerivDecl _ -> []
1630−
1631−instance ToHie (LFixitySig GhcRn) where
1632− toHie (L span sig) = concatM $ makeNode sig span : case sig of
1633− FixitySig _ vars _ ->
1634− [ toHie $ map (C Use) vars
1635− ]
1636− XFixitySig _ -> []
1637−
1638−instance ToHie (LDefaultDecl GhcRn) where
1639− toHie (L span decl) = concatM $ makeNode decl span : case decl of
1640− DefaultDecl _ typs ->
1641− [ toHie typs
1642− ]
1643− XDefaultDecl _ -> []
1644−
1645−instance ToHie (LForeignDecl GhcRn) where
1646− toHie (L span decl) = concatM $ makeNode decl span : case decl of
1647− ForeignImport {fd_name = name, fd_sig_ty = sig, fd_fi = fi} ->
1648− [ toHie $ C (ValBind RegularBind ModuleScope $ getRealSpan span) name
1649− , toHie $ TS (ResolvedScopes []) sig
1650− , toHie fi
1651− ]
1652− ForeignExport {fd_name = name, fd_sig_ty = sig, fd_fe = fe} ->
1653− [ toHie $ C Use name
1654− , toHie $ TS (ResolvedScopes []) sig
1655− , toHie fe
1656− ]
1657− XForeignDecl _ -> []
1658−
1659−instance ToHie ForeignImport where
1660− toHie (CImport (L a _) (L b _) _ _ (L c _)) = pure $ concat $
1661− [ locOnly a
1662− , locOnly b
1663− , locOnly c
1664− ]
1665−
1666−instance ToHie ForeignExport where
1667− toHie (CExport (L a _) (L b _)) = pure $ concat $
1668− [ locOnly a
1669− , locOnly b
1670− ]
1671−
1672−instance ToHie (LWarnDecls GhcRn) where
1673− toHie (L span decl) = concatM $ makeNode decl span : case decl of
1674− Warnings _ _ warnings ->
1675− [ toHie warnings
1676− ]
1677− XWarnDecls _ -> []
1678−
1679−instance ToHie (LWarnDecl GhcRn) where
1680− toHie (L span decl) = concatM $ makeNode decl span : case decl of
1681− Warning _ vars _ ->
1682− [ toHie $ map (C Use) vars
1683− ]
1684− XWarnDecl _ -> []
1685−
1686−instance ToHie (LAnnDecl GhcRn) where
1687− toHie (L span decl) = concatM $ makeNode decl span : case decl of
1688− HsAnnotation _ _ prov expr ->
1689− [ toHie prov
1690− , toHie expr
1691− ]
1692− XAnnDecl _ -> []
1693−
1694−instance ToHie (Context (Located a)) => ToHie (AnnProvenance a) where
1695− toHie (ValueAnnProvenance a) = toHie $ C Use a
1696− toHie (TypeAnnProvenance a) = toHie $ C Use a
1697− toHie ModuleAnnProvenance = pure []
1698−
1699−instance ToHie (LRuleDecls GhcRn) where
1700− toHie (L span decl) = concatM $ makeNode decl span : case decl of
1701− HsRules _ _ rules ->
1702− [ toHie rules
1703− ]
1704− XRuleDecls _ -> []
1705−
1706−instance ToHie (LRuleDecl GhcRn) where
1707− toHie (L _ (XRuleDecl _)) = pure []
1708− toHie (L span r@(HsRule _ rname _ tybndrs bndrs exprA exprB)) = concatM
1709− [ makeNode r span
1710− , pure $ locOnly $ getLoc rname
1711− , toHie $ fmap (tvScopes (ResolvedScopes []) scope) tybndrs
1712− , toHie $ map (RS $ mkScope span) bndrs
1713− , toHie exprA
1714− , toHie exprB
1715− ]
1716− where scope = bndrs_sc `combineScopes` exprA_sc `combineScopes` exprB_sc
1717− bndrs_sc = maybe NoScope mkLScope (listToMaybe bndrs)
1718− exprA_sc = mkLScope exprA
1719− exprB_sc = mkLScope exprB
1720−
1721−instance ToHie (RScoped (LRuleBndr GhcRn)) where
1722− toHie (RS sc (L span bndr)) = concatM $ makeNode bndr span : case bndr of
1723− RuleBndr _ var ->
1724− [ toHie $ C (ValBind RegularBind sc Nothing) var
1725− ]
1726− RuleBndrSig _ var typ ->
1727− [ toHie $ C (ValBind RegularBind sc Nothing) var
1728− , toHie $ TS (ResolvedScopes [sc]) typ
1729− ]
1730− XRuleBndr _ -> []
1731−
1732−instance ToHie (LImportDecl GhcRn) where
1733− toHie (L span decl) = concatM $ makeNode decl span : case decl of
1734− ImportDecl { ideclName = name, ideclAs = as, ideclHiding = hidden } ->
1735− [ toHie $ IEC Import name
1736− , toHie $ fmap (IEC ImportAs) as
1737− , maybe (pure []) goIE hidden
1738− ]
1739− XImportDecl _ -> []
1740− where
1741− goIE (hiding, (L sp liens)) = concatM $
1742− [ pure $ locOnly sp
1743− , toHie $ map (IEC c) liens
1744− ]
1745− where
1746− c = if hiding then ImportHiding else Import
1747−
1748−instance ToHie (IEContext (LIE GhcRn)) where
1749− toHie (IEC c (L span ie)) = concatM $ makeNode ie span : case ie of
1750− IEVar _ n ->
1751− [ toHie $ IEC c n
1752− ]
1753− IEThingAbs _ n ->
1754− [ toHie $ IEC c n
1755− ]
1756− IEThingAll _ n ->
1757− [ toHie $ IEC c n
1758− ]
1759− IEThingWith _ n _ ns flds ->
1760− [ toHie $ IEC c n
1761− , toHie $ map (IEC c) ns
1762− , toHie $ map (IEC c) flds
1763− ]
1764− IEModuleContents _ n ->
1765− [ toHie $ IEC c n
1766− ]
1767− IEGroup _ _ _ -> []
1768− IEDoc _ _ -> []
1769− IEDocNamed _ _ -> []
1770− XIE _ -> []
1771−
1772−instance ToHie (IEContext (LIEWrappedName Name)) where
1773− toHie (IEC c (L span iewn)) = concatM $ makeNode iewn span : case iewn of
1774− IEName n ->
1775− [ toHie $ C (IEThing c) n
1776− ]
1777− IEPattern p ->
1778− [ toHie $ C (IEThing c) p
1779− ]
1780− IEType n ->
1781− [ toHie $ C (IEThing c) n
1782− ]
1783−
1784−instance ToHie (IEContext (Located (FieldLbl Name))) where
1785− toHie (IEC c (L span lbl)) = concatM $ makeNode lbl span : case lbl of
1786− FieldLabel _ _ n ->
1787− [ toHie $ C (IEThing c) $ L span n
1788− ]
1789−
src/Development/IDE/Compat.hsdeleted−19
@@ -1,19 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−module Development.IDE.Compat
3− (
4− getProcessID
5− ) where
6−
7−#ifdef mingw32_HOST_OS
8−
9−import qualified System.Win32.Process as P (getCurrentProcessId)
10−getProcessID :: IO Int
11−getProcessID = fromIntegral <$> P.getCurrentProcessId
12−
13−#else
14−
15−import qualified System.Posix.Process as P (getProcessID)
16−getProcessID :: IO Int
17−getProcessID = fromIntegral <$> P.getProcessID
18−
19−#endif
src/Development/IDE/Core/Compile.hs+104−306
@@ -9,7 +9,6 @@
99-- Given a list of paths to find libraries, and a file to compile, produce a list of 'CoreModule' values.
1010module Development.IDE.Core.Compile
1111 ( TcModuleResult(..)
12− , RunSimplifier(..)
1312 , compileModule
1413 , parseModule
1514 , typecheckModule
@@ -17,18 +16,10 @@ module Development.IDE.Core.Compile
1716 , addRelativeImport
1817 , mkTcModuleResult
1918 , generateByteCode
20− , generateAndWriteHieFile
21− , generateAndWriteHiFile
22− , getModSummaryFromImports
23− , loadHieFile
24− , loadInterface
25− , loadDepModule
26− , loadModuleHome
2719 ) where
2820
2921import Development.IDE.Core.RuleTypes
3022import Development.IDE.Core.Preprocessor
31−import Development.IDE.Core.Shake
3223import Development.IDE.GHC.Error
3324import Development.IDE.GHC.Warnings
3425import Development.IDE.Types.Diagnostics
@@ -37,7 +28,6 @@ import Development.IDE.GHC.Util
3728import qualified GHC.LanguageExtensions.Type as GHC
3829import Development.IDE.Types.Options
3930import Development.IDE.Types.Location
40−import Outputable
4131
4232#if MIN_GHC_API_VERSION(8,6,0)
4333import DynamicLoading (initializePlugins)
@@ -46,60 +36,42 @@ import DynamicLoading (initializePlugins)
4636import GHC hiding (parseModule, typecheckModule)
4737import qualified Parser
4838import Lexer
49−#if MIN_GHC_API_VERSION(8,10,0)
50−#else
5139import ErrUtils
52−#endif
5340
54−import Finder
55−import qualified Development.IDE.GHC.Compat as GHC
56−import qualified Development.IDE.GHC.Compat as Compat
41+import qualified GHC
5742import GhcMonad
5843import GhcPlugins as GHC hiding (fst3, (<>))
5944import qualified HeaderInfo as Hdr
60−import HscMain (hscInteractive, hscSimplify)
61−import LoadIface (readIface)
62−import qualified Maybes
45+import HscMain (hscInteractive)
6346import MkIface
64−import NameCache
6547import StringBuffer as SB
66−import TcRnMonad (initIfaceLoad, tcg_th_coreplugins)
67−import TcIface (typecheckIface)
6848import TidyPgm
6949
70−import Control.Exception.Safe
7150import Control.Monad.Extra
7251import Control.Monad.Except
7352import Control.Monad.Trans.Except
74−import Data.Bifunctor (first, second)
53+import Data.Function
54+import Data.Ord
7555import qualified Data.Text as T
7656import Data.IORef
7757import Data.List.Extra
7858import Data.Maybe
59+import Data.Tuple.Extra
7960import qualified Data.Map.Strict as Map
8061import System.FilePath
81−import System.Directory
82−import System.IO.Extra
83−import Data.Either.Extra (maybeToEither)
84−import Control.DeepSeq (rnf)
85−import Control.Exception (evaluate)
86−import Exception (ExceptionMonad)
8762
8863
89−-- | Given a string buffer, return the string (after preprocessing) and the 'ParsedModule'.
64+-- | Given a string buffer, return a pre-processed @ParsedModule@.
9065parseModule
9166 :: IdeOptions
9267 -> HscEnv
93− -> [PackageName]
9468 -> FilePath
9569 -> Maybe SB.StringBuffer
96− -> IO (IdeResult (StringBuffer, ParsedModule))
97−parseModule IdeOptions{..} env comp_pkgs filename mbContents =
98− fmap (either (, Nothing) id) $
99− evalGhcEnv env $ runExceptT $ do
100− (contents, dflags) <- preprocessor filename mbContents
101− (diag, modu) <- parseFileContents optPreprocessor dflags comp_pkgs filename contents
102− return (diag, Just (contents, modu))
70+ -> IO ([FileDiagnostic], Maybe ParsedModule)
71+parseModule IdeOptions{..} env file =
72+ fmap (either (, Nothing) (second Just)) .
73+ -- We need packages since imports fail to resolve otherwise.
74+ runGhcEnv env . runExceptT . parseFileContents optPreprocessor file
10375
10476
10577-- | Given a package identifier, what packages does it depend on
@@ -110,39 +82,34 @@ computePackageDeps
11082computePackageDeps env pkg = do
11183 let dflags = hsc_dflags env
11284 case lookupInstalledPackage dflags pkg of
113− Nothing -> return $ Left [ideErrorText (toNormalizedFilePath' noFilePath) $
85+ Nothing -> return $ Left [ideErrorText (toNormalizedFilePath noFilePath) $
11486 T.pack $ "unknown package: " ++ show pkg]
11587 Just pkgInfo -> return $ Right $ depends pkgInfo
11688
117−typecheckModule :: IdeDefer
118− -> HscEnv
119− -> [(ModSummary, (ModIface, Maybe Linkable))]
120− -> ParsedModule
121− -> IO (IdeResult (HscEnv, TcModuleResult))
122−typecheckModule (IdeDefer defer) hsc depsIn pm = do
123− fmap (either (, Nothing) (second Just . sequence) . sequence) $
124− runGhcEnv hsc $
125− catchSrcErrors "typecheck" $ do
126− -- Currently GetDependencies returns things in topological order so A comes before B if A imports B.
127− -- We need to reverse this as GHC gets very unhappy otherwise and complains about broken interfaces.
128− -- Long-term we might just want to change the order returned by GetDependencies
129− let deps = reverse depsIn
130−
131− setupFinderCache (map fst deps)
132−
133− let modSummary = pm_mod_summary pm
134− dflags = ms_hspp_opts modSummary
135−
136− mapM_ (uncurry loadDepModule . snd) deps
137− modSummary' <- initPlugins modSummary
138− (warnings, tcm) <- withWarnings "typecheck" $ \tweak ->
139− GHC.typecheckModule $ enableTopLevelWarnings
140− $ demoteIfDefer pm{pm_mod_summary = tweak modSummary'}
141− tcm2 <- mkTcModuleResult tcm
142− let errorPipeline = unDefer . hideDiag dflags
143− return (map errorPipeline warnings, tcm2)
144− where
145− demoteIfDefer = if defer then demoteTypeErrorsToWarnings else id
89+
90+-- | Typecheck a single module using the supplied dependencies and packages.
91+typecheckModule
92+ :: IdeDefer
93+ -> HscEnv
94+ -> [TcModuleResult]
95+ -> ParsedModule
96+ -> IO ([FileDiagnostic], Maybe TcModuleResult)
97+typecheckModule (IdeDefer defer) packageState deps pm =
98+ let demoteIfDefer = if defer then demoteTypeErrorsToWarnings else id
99+ in
100+ fmap (either (, Nothing) (second Just)) $
101+ runGhcEnv packageState $
102+ catchSrcErrors "typecheck" $ do
103+ setupEnv deps
104+ let modSummary = pm_mod_summary pm
105+ dflags = ms_hspp_opts modSummary
106+ modSummary' <- initPlugins modSummary
107+ (warnings, tcm) <- withWarnings "typecheck" $ \tweak ->
108+ GHC.typecheckModule $ enableTopLevelWarnings
109+ $ demoteIfDefer pm{pm_mod_summary = tweak modSummary'}
110+ tcm2 <- mkTcModuleResult tcm
111+ let errorPipeline = unDefer . hideDiag dflags
112+ return (map errorPipeline warnings, tcm2)
146113
147114initPlugins :: GhcMonad m => ModSummary -> m ModSummary
148115initPlugins modSummary = do
@@ -154,25 +121,18 @@ initPlugins modSummary = do
154121 return modSummary
155122#endif
156123
157−-- | Whether we should run the -O0 simplifier when generating core.
158−--
159−-- This is required for template Haskell to work but we disable this in DAML.
160−-- See #256
161−newtype RunSimplifier = RunSimplifier Bool
162−
163124-- | Compile a single type-checked module to a 'CoreModule' value, or
164125-- provide errors.
165126compileModule
166− :: RunSimplifier
167− -> HscEnv
168− -> [(ModSummary, HomeModInfo)]
127+ :: HscEnv
128+ -> [TcModuleResult]
169129 -> TcModuleResult
170− -> IO (IdeResult (SafeHaskellMode, CgGuts, ModDetails))
171−compileModule (RunSimplifier simplify) packageState deps tmr =
130+ -> IO ([FileDiagnostic], Maybe (SafeHaskellMode, CgGuts, ModDetails))
131+compileModule packageState deps tmr =
172132 fmap (either (, Nothing) (second Just)) $
173− evalGhcEnv packageState $
133+ runGhcEnv packageState $
174134 catchSrcErrors "compile" $ do
175− setupEnv (deps ++ [(tmrModSummary tmr, tmrModInfo tmr)])
135+ setupEnv (deps ++ [tmr])
176136
177137 let tm = tmrModule tmr
178138 session <- getSession
@@ -181,30 +141,20 @@ compileModule (RunSimplifier simplify) packageState deps tmr =
181141 let pm' = pm{pm_mod_summary = tweak $ pm_mod_summary pm}
182142 let tm' = tm{tm_parsed_module = pm'}
183143 GHC.dm_core_module <$> GHC.desugarModule tm'
184− let tc_result = fst (tm_internals_ (tmrModule tmr))
185− desugared_guts <-
186− if simplify
187− then do
188− plugins <- liftIO $ readIORef (tcg_th_coreplugins tc_result)
189− liftIO $ hscSimplify session plugins desugar
190− else pure desugar
144+
191145 -- give variables unique OccNames
192− (guts, details) <- liftIO $ tidyProgram session desugared_guts
146+ (guts, details) <- liftIO $ tidyProgram session desugar
193147 return (map snd warnings, (mg_safe_haskell desugar, guts, details))
194148
195−generateByteCode :: HscEnv -> [(ModSummary, HomeModInfo)] -> TcModuleResult -> CgGuts -> IO (IdeResult Linkable)
149+generateByteCode :: HscEnv -> [TcModuleResult] -> TcModuleResult -> CgGuts -> IO ([FileDiagnostic], Maybe Linkable)
196150generateByteCode hscEnv deps tmr guts =
197151 fmap (either (, Nothing) (second Just)) $
198− evalGhcEnv hscEnv $
152+ runGhcEnv hscEnv $
199153 catchSrcErrors "bytecode" $ do
200− setupEnv (deps ++ [(tmrModSummary tmr, tmrModInfo tmr)])
154+ setupEnv (deps ++ [tmr])
201155 session <- getSession
202156 (warnings, (_, bytecode, sptEntries)) <- withWarnings "bytecode" $ \tweak ->
203−#if MIN_GHC_API_VERSION(8,10,0)
204− liftIO $ hscInteractive session guts (GHC.ms_location $ tweak $ GHC.pm_mod_summary $ GHC.tm_parsed_module $ tmrModule tmr)
205−#else
206− liftIO $ hscInteractive session guts (tweak $ GHC.pm_mod_summary $ GHC.tm_parsed_module $ tmrModule tmr)
207−#endif
157+ liftIO $ hscInteractive session guts (tweak $ GHC.pm_mod_summary $ GHC.tm_parsed_module $ tmrModule tmr)
208158 let summary = pm_mod_summary $ tm_parsed_module $ tmrModule tmr
209159 let unlinked = BCOs bytecode sptEntries
210160 let linkable = LM (ms_hs_date summary) (ms_mod summary) [unlinked]
@@ -226,10 +176,9 @@ demoteTypeErrorsToWarnings =
226176enableTopLevelWarnings :: ParsedModule -> ParsedModule
227177enableTopLevelWarnings =
228178 (update_pm_mod_summary . update_hspp_opts)
229− ((`wopt_set` Opt_WarnMissingPatternSynonymSignatures) .
230− (`wopt_set` Opt_WarnMissingSignatures))
179+ (`wopt_set` Opt_WarnMissingSignatures)
231180 -- the line below would show also warnings for let bindings without signature
232− -- ((`wopt_set` Opt_WarnMissingSignatures) . (`wopt_set` Opt_WarnMissingLocalSignatures)))
181+ -- ((`wopt_set` Opt_WarnMissingSignatures) . (`wopt_set` Opt_WarnMissingLocalSignatures))
233182
234183update_hspp_opts :: (DynFlags -> DynFlags) -> ModSummary -> ModSummary
235184update_hspp_opts up ms = ms{ms_hspp_opts = up $ ms_hspp_opts ms}
@@ -253,9 +202,9 @@ upgradeWarningToError (nfp, sh, fd) =
253202hideDiag :: DynFlags -> (WarnReason, FileDiagnostic) -> (WarnReason, FileDiagnostic)
254203hideDiag originalFlags (Reason warning, (nfp, _sh, fd))
255204 | not (wopt warning originalFlags) = (Reason warning, (nfp, HideDiag, fd))
256−hideDiag _originalFlags t = t
205+hideDiag _originalFlags t = t
257206
258−addRelativeImport :: NormalizedFilePath -> ModuleName -> DynFlags -> DynFlags
207+addRelativeImport :: NormalizedFilePath -> ParsedModule -> DynFlags -> DynFlags
259208addRelativeImport fp modu dflags = dflags
260209 {importPaths = nubOrd $ maybeToList (moduleImportPath fp modu) ++ importPaths dflags}
261210
@@ -265,81 +214,25 @@ mkTcModuleResult
265214 -> m TcModuleResult
266215mkTcModuleResult tcm = do
267216 session <- getSession
268− let sf = modInfoSafe (tm_checked_module_info tcm)
269−#if MIN_GHC_API_VERSION(8,10,0)
270− iface <- liftIO $ mkIfaceTc session sf details tcGblEnv
271−#else
272− (iface, _) <- liftIO $ mkIfaceTc session Nothing sf details tcGblEnv
273−#endif
217+ (iface, _) <- liftIO $ mkIfaceTc session Nothing Sf_None details tcGblEnv
274218 let mod_info = HomeModInfo iface details Nothing
275219 return $ TcModuleResult tcm mod_info
276220 where
277221 (tcGblEnv, details) = tm_internals_ tcm
278222
279−atomicFileWrite :: FilePath -> (FilePath -> IO a) -> IO ()
280−atomicFileWrite targetPath write = do
281− let dir = takeDirectory targetPath
282− createDirectoryIfMissing True dir
283− (tempFilePath, cleanUp) <- newTempFileWithin dir
284− (write tempFilePath >> renameFile tempFilePath targetPath) `onException` cleanUp
285−
286−generateAndWriteHieFile :: HscEnv -> TypecheckedModule -> IO [FileDiagnostic]
287−generateAndWriteHieFile hscEnv tcm =
288− handleGenerationErrors dflags "extended interface generation" $ do
289− case tm_renamed_source tcm of
290− Just rnsrc -> do
291− hf <- runHsc hscEnv $
292− GHC.mkHieFile mod_summary (fst $ tm_internals_ tcm) rnsrc ""
293− atomicFileWrite targetPath $ flip GHC.writeHieFile hf
294− _ ->
295− return ()
296− where
297− dflags = hsc_dflags hscEnv
298− mod_summary = pm_mod_summary $ tm_parsed_module tcm
299− mod_location = ms_location mod_summary
300− targetPath = Compat.ml_hie_file mod_location
301−
302−generateAndWriteHiFile :: HscEnv -> TcModuleResult -> IO [FileDiagnostic]
303−generateAndWriteHiFile hscEnv tc =
304− handleGenerationErrors dflags "interface generation" $ do
305− atomicFileWrite targetPath $ \fp ->
306− writeIfaceFile dflags fp modIface
307− where
308− modIface = hm_iface $ tmrModInfo tc
309− modSummary = tmrModSummary tc
310− targetPath = withBootSuffix $ ml_hi_file $ ms_location $ tmrModSummary tc
311− withBootSuffix = case ms_hsc_src modSummary of
312− HsBootFile -> addBootSuffix
313− _ -> id
314− dflags = hsc_dflags hscEnv
315−
316−handleGenerationErrors :: DynFlags -> T.Text -> IO () -> IO [FileDiagnostic]
317−handleGenerationErrors dflags source action =
318− action >> return [] `catches`
319− [ Handler $ return . diagFromGhcException source dflags
320− , Handler $ return . diagFromString source DsError (noSpan "<internal>")
321− . (("Error during " ++ T.unpack source) ++) . show @SomeException
322− ]
323−
324−
325223-- | Setup the environment that GHC needs according to our
326224-- best understanding (!)
327−--
328−-- This involves setting up the finder cache and populating the
329−-- HPT.
330−setupEnv :: GhcMonad m => [(ModSummary, HomeModInfo)] -> m ()
331−setupEnv tms = do
332− setupFinderCache (map fst tms)
333− -- load dependent modules, which must be in topological order.
334− modifySession $ \e ->
335− foldl' (\e (_, hmi) -> loadModuleHome hmi e) e tms
225+setupEnv :: GhcMonad m => [TcModuleResult] -> m ()
226+setupEnv tmsIn = do
227+ -- if both a .hs-boot file and a .hs file appear here, we want to make sure that the .hs file
228+ -- takes precedence, so put the .hs-boot file earlier in the list
229+ let isSourceFile = (==HsBootFile) . ms_hsc_src . pm_mod_summary . tm_parsed_module . tmrModule
230+ tms = sortBy (compare `on` Down . isSourceFile) tmsIn
336231
337−-- | Initialise the finder cache, dependencies should be topologically
338−-- sorted.
339−setupFinderCache :: GhcMonad m => [ModSummary] -> m ()
340−setupFinderCache mss = do
341232 session <- getSession
342233
234+ let mss = map (pm_mod_summary . tm_parsed_module . tmrModule) tms
235+
343236 -- set the target and module graph in the session
344237 let graph = mkModuleGraph mss
345238 setSession session { hsc_mod_graph = graph }
@@ -358,40 +251,26 @@ setupFinderCache mss = do
358251 newFinderCacheVar <- liftIO $ newIORef $! newFinderCache
359252 modifySession $ \s -> s { hsc_FC = newFinderCacheVar }
360253
254+ -- load dependent modules, which must be in topological order.
255+ mapM_ loadModuleHome tms
256+
361257
362258-- | Load a module, quickly. Input doesn't need to be desugared.
363259-- A module must be loaded before dependent modules can be typechecked.
364260-- This variant of loadModuleHome will *never* cause recompilation, it just
365261-- modifies the session.
366−--
367−-- The order modules are loaded is important when there are hs-boot files.
368−-- In particular you should make sure to load the .hs version of a file after the
369−-- .hs-boot version.
370262loadModuleHome
371− :: HomeModInfo
372− -> HscEnv
373− -> HscEnv
374−loadModuleHome mod_info e =
375− e { hsc_HPT = addToHpt (hsc_HPT e) mod_name mod_info }
376− where
377− mod_name = moduleName $ mi_module $ hm_iface mod_info
378−
379−-- | Load module interface.
380−loadDepModuleIO :: ModIface -> Maybe Linkable -> HscEnv -> IO HscEnv
381−loadDepModuleIO iface linkable hsc = do
382− details <- liftIO $ fixIO $ \details -> do
383− let hsc' = hsc { hsc_HPT = addToHpt (hsc_HPT hsc) mod (HomeModInfo iface details linkable) }
384− initIfaceLoad hsc' (typecheckIface iface)
385− let mod_info = HomeModInfo iface details linkable
386− return $ loadModuleHome mod_info hsc
387− where
388− mod = moduleName $ mi_module iface
263+ :: (GhcMonad m)
264+ => TcModuleResult
265+ -> m ()
266+loadModuleHome tmr = modifySession $ \e ->
267+ e { hsc_HPT = addToHpt (hsc_HPT e) mod mod_info }
268+ where
269+ ms = pm_mod_summary . tm_parsed_module . tmrModule $ tmr
270+ mod_info = tmrModInfo tmr
271+ mod = ms_mod_name ms
272+
389273
390−loadDepModule :: GhcMonad m => ModIface -> Maybe Linkable -> m ()
391−loadDepModule iface linkable = do
392− e <- getSession
393− e' <- liftIO $ loadDepModuleIO iface linkable e
394− setSession e'
395274
396275-- | GhcMonad function to chase imports of a module given as a StringBuffer. Returns given module's
397276-- name and its imports.
@@ -412,18 +291,30 @@ getImportsParsed dflags (L loc parsed) = do
412291 , GHC.moduleNameString (GHC.unLoc $ ideclName i) /= "GHC.Prim"
413292 ])
414293
294+
415295-- | Produce a module summary from a StringBuffer.
416296getModSummaryFromBuffer
417297 :: GhcMonad m
418298 => FilePath
299+ -> SB.StringBuffer
419300 -> DynFlags
420301 -> GHC.ParsedSource
421302 -> ExceptT [FileDiagnostic] m ModSummary
422−getModSummaryFromBuffer fp dflags parsed = do
303+getModSummaryFromBuffer fp contents dflags parsed = do
423304 (modName, imports) <- liftEither $ getImportsParsed dflags parsed
424305
425− modLoc <- liftIO $ mkHomeModLocation dflags modName fp
426− let InstalledUnitId unitId = thisInstalledUnitId dflags
306+ let modLoc = ModLocation
307+ { ml_hs_file = Just fp
308+ , ml_hi_file = derivedFile "hi"
309+ , ml_obj_file = derivedFile "o"
310+#if MIN_GHC_API_VERSION(8,8,0)
311+ , ml_hie_file = derivedFile "hie"
312+#endif
313+ -- This does not consider the dflags configuration
314+ -- (-osuf and -hisuf, object and hi dir.s).
315+ -- However, we anyway don't want to generate them.
316+ }
317+ InstalledUnitId unitId = thisInstalledUnitId dflags
427318 return $ ModSummary
428319 { ms_mod = mkModule (fsToUnitId unitId) modName
429320 , ms_location = modLoc
@@ -435,7 +326,7 @@ getModSummaryFromBuffer fp dflags parsed = do
435326 , ms_textual_imps = [imp | (False, imp) <- imports]
436327 , ms_hspp_file = fp
437328 , ms_hspp_opts = dflags
438− , ms_hspp_buf = Nothing
329+ , ms_hspp_buf = Just contents
439330
440331 -- defaults:
441332 , ms_hsc_src = sourceType
@@ -448,71 +339,27 @@ getModSummaryFromBuffer fp dflags parsed = do
448339 , ms_parsed_mod = Nothing
449340 }
450341 where
451− sourceType = if "-boot" `isSuffixOf` takeExtension fp then HsBootFile else HsSrcFile
452−
453−-- | Given a buffer, env and filepath, produce a module summary by parsing only the imports.
454−-- Runs preprocessors as needed.
455−getModSummaryFromImports
456− :: (HasDynFlags m, ExceptionMonad m, MonadIO m)
457− => FilePath
458− -> Maybe SB.StringBuffer
459− -> ExceptT [FileDiagnostic] m ModSummary
460−getModSummaryFromImports fp contents = do
461− (contents, dflags) <- preprocessor fp contents
462− (srcImports, textualImports, L _ moduleName) <-
463− ExceptT $ liftIO $ first (diagFromErrMsgs "parser" dflags) <$> GHC.getHeaderImports dflags contents fp fp
464−
465− -- Force bits that might keep the string buffer and DynFlags alive unnecessarily
466− liftIO $ evaluate $ rnf srcImports
467− liftIO $ evaluate $ rnf textualImports
468−
469− modLoc <- liftIO $ mkHomeModLocation dflags moduleName fp
470−
471− let mod = mkModule (thisPackage dflags) moduleName
472− sourceType = if "-boot" `isSuffixOf` takeExtension fp then HsBootFile else HsSrcFile
473− summary =
474− ModSummary
475− { ms_mod = mod
476−#if MIN_GHC_API_VERSION(8,8,0)
477− , ms_hie_date = Nothing
478−#endif
479− , ms_hs_date = error "Rules should not depend on ms_hs_date"
480− -- When we are working with a virtual file we do not have a file date.
481− -- To avoid silent issues where something is not processed because the date
482− -- has not changed, we make sure that things blow up if they depend on the date.
483− , ms_hsc_src = sourceType
484− , ms_hspp_buf = Nothing
485− , ms_hspp_file = fp
486− , ms_hspp_opts = dflags
487− , ms_iface_date = Nothing
488− , ms_location = modLoc
489− , ms_obj_date = Nothing
490− , ms_parsed_mod = Nothing
491− , ms_srcimps = srcImports
492− , ms_textual_imps = textualImports
493− }
494− return summary
495−
496−
497−-- | Given a buffer, flags, and file path, produce a
498−-- parsed module (or errors) and any parse warnings. Does not run any preprocessors
342+ (sourceType, derivedFile) =
343+ let (stem, ext) = splitExtension fp in
344+ if "-boot" `isSuffixOf` ext
345+ then (HsBootFile, \newExt -> stem <.> newExt ++ "-boot")
346+ else (HsSrcFile , \newExt -> stem <.> newExt)
347+
348+
349+-- | Given a buffer, flags, file path and module summary, produce a
350+-- parsed module (or errors) and any parse warnings.
499351parseFileContents
500352 :: GhcMonad m
501353 => (GHC.ParsedSource -> IdePreprocessedSource)
502− -> DynFlags -- ^ flags to use
503− -> [PackageName] -- ^ The package imports to ignore
504354 -> FilePath -- ^ the filename (for source locations)
505− -> SB.StringBuffer -- ^ Haskell module source text (full Unicode is supported)
355+ -> Maybe SB.StringBuffer -- ^ Haskell module source text (full Unicode is supported)
506356 -> ExceptT [FileDiagnostic] m ([FileDiagnostic], ParsedModule)
507−parseFileContents customPreprocessor dflags comp_pkgs filename contents = do
357+parseFileContents customPreprocessor filename mbContents = do
358+ (contents, dflags) <- preprocessor filename mbContents
508359 let loc = mkRealSrcLoc (mkFastString filename) 1 1
509360 case unP Parser.parseModule (mkPState dflags contents loc) of
510−#if MIN_GHC_API_VERSION(8,10,0)
511− PFailed pst -> throwE $ diagFromErrMsgs "parser" dflags $ getErrorMessages pst dflags
512−#else
513361 PFailed _ locErr msgErr ->
514362 throwE $ diagFromErrMsg "parser" dflags $ mkPlainErrMsg dflags locErr msgErr
515−#endif
516363 POk pst rdr_module ->
517364 let hpm_annotations =
518365 (Map.fromListWith (++) $ annotations pst,
@@ -536,63 +383,14 @@ parseFileContents customPreprocessor dflags comp_pkgs filename contents = do
536383 -- Ok, we got here. It's safe to continue.
537384 let IdePreprocessedSource preproc_warns errs parsed = customPreprocessor rdr_module
538385 unless (null errs) $ throwE $ diagFromStrings "parser" DsError errs
539− let parsed' = removePackageImports comp_pkgs parsed
540386 let preproc_warnings = diagFromStrings "parser" DsWarning preproc_warns
541− ms <- getModSummaryFromBuffer filename dflags parsed'
387+ ms <- getModSummaryFromBuffer filename contents dflags parsed
542388 let pm =
543389 ParsedModule {
544390 pm_mod_summary = ms
545− , pm_parsed_source = parsed'
391+ , pm_parsed_source = parsed
546392 , pm_extra_src_files=[] -- src imports not allowed
547393 , pm_annotations = hpm_annotations
548394 }
549395 warnings = diagFromErrMsgs "parser" dflags warns
550396 pure (warnings ++ preproc_warnings, pm)
551−
552−
553−-- | After parsing the module remove all package imports referring to
554−-- these packages as we have already dealt with what they map to.
555−removePackageImports :: [PackageName] -> GHC.ParsedSource -> GHC.ParsedSource
556−removePackageImports pkgs (L l h@HsModule {hsmodImports} ) = L l (h { hsmodImports = imports' })
557− where
558− imports' = map do_one_import hsmodImports
559− do_one_import (L l i@ImportDecl{ideclPkgQual}) =
560− case PackageName . sl_fs <$> ideclPkgQual of
561− Just pn | pn `elem` pkgs -> L l (i { ideclPkgQual = Nothing })
562− _ -> L l i
563−#if MIN_GHC_API_VERSION(8,6,0)
564− do_one_import l = l
565−#endif
566−
567−loadHieFile :: FilePath -> IO GHC.HieFile
568−loadHieFile f = do
569− u <- mkSplitUniqSupply 'a'
570− let nameCache = initNameCache u []
571− fmap (GHC.hie_file_result . fst) $ GHC.readHieFile nameCache f
572−
573−-- | Retuns an up-to-date module interface if available.
574−-- Assumes file exists.
575−-- Requires the 'HscEnv' to be set up with dependencies
576−loadInterface
577− :: HscEnv
578− -> ModSummary
579− -> [HiFileResult]
580− -> IO (Either String ModIface)
581−loadInterface session ms deps = do
582− let hiFile = case ms_hsc_src ms of
583− HsBootFile -> addBootSuffix (ml_hi_file $ ms_location ms)
584− _ -> ml_hi_file $ ms_location ms
585− r <- initIfaceLoad session $ readIface (ms_mod ms) hiFile
586− case r of
587− Maybes.Succeeded iface -> do
588− session' <- foldM (\e d -> loadDepModuleIO (hirModIface d) Nothing e) session deps
589− (reason, iface') <- checkOldIface session' ms SourceUnmodified (Just iface)
590− return $ maybeToEither (showReason reason) iface'
591− Maybes.Failed err -> do
592− let errMsg = showSDoc (hsc_dflags session) err
593− return $ Left errMsg
594−
595−showReason :: RecompileRequired -> String
596−showReason MustCompile = "Stale"
597−showReason (RecompBecause reason) = "Stale (" ++ reason ++ ")"
598−showReason UpToDate = "Up to date"
src/Development/IDE/Plugin/Completions/Logic.hs → src/Development/IDE/Core/Completions.hsrenamed+49−45
@@ -1,7 +1,6 @@
11{-# LANGUAGE CPP #-}
2−#include "ghc-api-version.h"
32-- Mostly taken from "haskell-ide-engine"
4−module Development.IDE.Plugin.Completions.Logic (
3+module Development.IDE.Core.Completions (
54 CachedCompletions
65, cacheDataProducer
76, WithSnippets(..)
@@ -11,7 +10,7 @@ module Development.IDE.Plugin.Completions.Logic (
1110import Control.Applicative
1211import Data.Char (isSpace, isUpper)
1312import Data.Generics
14−import Data.List.Extra as List hiding (stripPrefix)
13+import Data.List as List hiding (stripPrefix)
1514import qualified Data.Map as Map
1615import Data.Maybe (fromMaybe, mapMaybe)
1716import qualified Data.Text as T
@@ -26,17 +25,11 @@ import Type
2625import Var
2726import Packages
2827import DynFlags
29−#if MIN_GHC_API_VERSION(8,10,0)
30−import Predicate (isDictTy)
31−import GHC.Platform
32−import Pair
33−import Coercion
34−#endif
3528
3629import Language.Haskell.LSP.Types
3730import Language.Haskell.LSP.Types.Capabilities
3831import qualified Language.Haskell.LSP.VFS as VFS
39−import Development.IDE.Plugin.Completions.Types
32+import Development.IDE.Core.CompletionsTypes
4033import Development.IDE.Spans.Documentation
4134import Development.IDE.GHC.Error
4235import Development.IDE.Types.Options
@@ -132,7 +125,7 @@ occNameToComKind ty oc
132125
133126mkCompl :: IdeOptions -> CompItem -> CompletionItem
134127mkCompl IdeOptions{..} CI{origName,importedFrom,thingType,label,isInfix,docs} =
135− CompletionItem label kind (List []) ((colon <>) <$> typeText)
128+ CompletionItem label kind ((colon <>) <$> typeText)
136129 (Just $ CompletionDocMarkup $ MarkupContent MkMarkdown $ T.intercalate sectionSeparator docs')
137130 Nothing Nothing Nothing Nothing (Just insertText) (Just Snippet)
138131 Nothing Nothing Nothing Nothing Nothing
@@ -162,7 +155,7 @@ getArgText typ = argText
162155 where
163156 argTypes = getArgs typ
164157 argText :: T.Text
165− argText = mconcat $ List.intersperse " " $ zipWithFrom snippet 1 argTypes
158+ argText = mconcat $ List.intersperse " " $ zipWith snippet [1..] argTypes
166159 snippet :: Int -> Type -> T.Text
167160 snippet i t = T.pack $ "${" <> show i <> ":" <> showGhc t <> "}"
168161 getArgs :: Type -> [Type]
@@ -176,44 +169,38 @@ getArgText typ = argText
176169 then getArgs ret
177170 else Prelude.filter (not . isDictTy) args
178171 | isPiTy t = getArgs $ snd (splitPiTys t)
179−#if MIN_GHC_API_VERSION(8,10,0)
180− | Just (Pair _ t) <- coercionKind <$> isCoercionTy_maybe t
181− = getArgs t
182−#else
183172 | isCoercionTy t = maybe [] (getArgs . snd) (splitCoercionType_maybe t)
184−#endif
185173 | otherwise = []
186174
187175mkModCompl :: T.Text -> CompletionItem
188176mkModCompl label =
189− CompletionItem label (Just CiModule) (List []) Nothing
177+ CompletionItem label (Just CiModule) Nothing
190178 Nothing Nothing Nothing Nothing Nothing Nothing Nothing
191179 Nothing Nothing Nothing Nothing Nothing
192180
193181mkImportCompl :: T.Text -> T.Text -> CompletionItem
194182mkImportCompl enteredQual label =
195− CompletionItem m (Just CiModule) (List []) (Just label)
183+ CompletionItem m (Just CiModule) (Just label)
196184 Nothing Nothing Nothing Nothing Nothing Nothing Nothing
197185 Nothing Nothing Nothing Nothing Nothing
198− where
186+ where
199187 m = fromMaybe "" (T.stripPrefix enteredQual label)
200188
201189mkExtCompl :: T.Text -> CompletionItem
202190mkExtCompl label =
203− CompletionItem label (Just CiKeyword) (List []) Nothing
191+ CompletionItem label (Just CiKeyword) Nothing
204192 Nothing Nothing Nothing Nothing Nothing Nothing Nothing
205193 Nothing Nothing Nothing Nothing Nothing
206194
207195mkPragmaCompl :: T.Text -> T.Text -> CompletionItem
208196mkPragmaCompl label insertText =
209− CompletionItem label (Just CiKeyword) (List []) Nothing
197+ CompletionItem label (Just CiKeyword) Nothing
210198 Nothing Nothing Nothing Nothing Nothing (Just insertText) (Just Snippet)
211199 Nothing Nothing Nothing Nothing Nothing
212200
213−cacheDataProducer :: HscEnv -> TypecheckedModule -> [ParsedModule] -> IO CachedCompletions
214−cacheDataProducer packageState tm deps = do
201+cacheDataProducer :: HscEnv -> DynFlags -> TypecheckedModule -> [TypecheckedModule] -> IO CachedCompletions
202+cacheDataProducer packageState dflags tm tcs = do
215203 let parsedMod = tm_parsed_module tm
216− dflags = hsc_dflags packageState
217204 curMod = moduleName $ ms_mod $ pm_mod_summary parsedMod
218205 Just (_,limports,_,_) = tm_renamed_source tm
219206
@@ -270,18 +257,18 @@ cacheDataProducer packageState tm deps = do
270257 let typ = Just $ varType var
271258 name = Var.varName var
272259 label = T.pack $ showGhc name
273− docs <- evalGhcEnv packageState $ getDocumentationTryGhc (tm_parsed_module tm : deps) name
260+ docs <- runGhcEnv packageState $ getDocumentationTryGhc (tm:tcs) name
274261 return $ CI name (showModName curMod) typ label Nothing docs
275262
276263 toCompItem :: ModuleName -> Name -> IO CompItem
277264 toCompItem mn n = do
278− docs <- evalGhcEnv packageState $ getDocumentationTryGhc (tm_parsed_module tm : deps) n
265+ docs <- runGhcEnv packageState $ getDocumentationTryGhc (tm:tcs) n
279266-- lookupName uses runInteractiveHsc, i.e., GHCi stuff which does not work with GHCi
280267-- and leads to fun errors like "Cannot continue after interface file error".
281268#ifdef GHC_LIB
282269 let ty = Right Nothing
283270#else
284− ty <- evalGhcEnv packageState $ catchSrcErrors "completion" $ do
271+ ty <- runGhcEnv packageState $ catchSrcErrors "completion" $ do
285272 name' <- lookupName n
286273 return $ name' >>= safeTyThingType
287274#endif
@@ -304,16 +291,19 @@ toggleSnippets ClientCapabilities { _textDocument } (WithSnippets with) x
304291 | otherwise = x { _insertTextFormat = Just PlainText
305292 , _insertText = Nothing
306293 }
307− where supported = Just True == (_textDocument >>= _completion >>= _completionItem >>= _snippetSupport)
294+ where supported = fromMaybe False (_textDocument >>= _completion >>= _completionItem >>= _snippetSupport)
308295
309296-- | Returns the cached completions for the given module and position.
310−getCompletions :: IdeOptions -> CachedCompletions -> ParsedModule -> VFS.PosPrefixInfo -> ClientCapabilities -> WithSnippets -> IO [CompletionItem]
297+getCompletions :: IdeOptions -> CachedCompletions -> TypecheckedModule -> VFS.PosPrefixInfo -> ClientCapabilities -> WithSnippets -> IO [CompletionItem]
311298getCompletions ideOpts CC { allModNamesAsNS, unqualCompls, qualCompls, importableModules }
312− pm prefixInfo caps withSnippets = do
299+ tm prefixInfo caps withSnippets = do
313300 let VFS.PosPrefixInfo { VFS.fullLine, VFS.prefixModule, VFS.prefixText } = prefixInfo
314301 enteredQual = if T.null prefixModule then "" else prefixModule <> "."
315302 fullPrefix = enteredQual <> prefixText
316303
304+ -- default to value context if no explicit context
305+ context = fromMaybe ValueContext $ getCContext pos (tm_parsed_module tm)
306+
317307 {- correct the position by moving 'foo :: Int -> String -> '
318308 ^
319309 to 'foo :: Int -> String -> '
@@ -342,11 +332,10 @@ getCompletions ideOpts CC { allModNamesAsNS, unqualCompls, qualCompls, importabl
342332 where
343333 isTypeCompl = isTcOcc . occName . origName
344334 -- completions specific to the current context
345− ctxCompls' = case getCContext pos pm of
346− Nothing -> compls
347− Just TypeContext -> filter isTypeCompl compls
348− Just ValueContext -> filter (not . isTypeCompl) compls
349− Just _ -> filter (not . isTypeCompl) compls
335+ ctxCompls' = case context of
336+ TypeContext -> filter isTypeCompl compls
337+ ValueContext -> filter (not . isTypeCompl) compls
338+ _ -> filter (not . isTypeCompl) compls
350339 -- Add whether the text to insert has backticks
351340 ctxCompls = map (\comp -> comp { isInfix = infixCompls }) ctxCompls'
352341
@@ -372,9 +361,7 @@ getCompletions ideOpts CC { allModNamesAsNS, unqualCompls, qualCompls, importabl
372361 filtImportCompls = filtListWith (mkImportCompl enteredQual) importableModules
373362 filtPragmaCompls = filtListWithSnippet mkPragmaCompl validPragmas
374363 filtOptsCompls = filtListWith mkExtCompl
375− filtKeywordCompls
376− | T.null prefixModule = filtListWith mkExtCompl (optKeywords ideOpts)
377− | otherwise = []
364+ filtKeywordCompls = if T.null prefixModule then filtListWith mkExtCompl keywords else []
378365
379366 stripLeading :: Char -> String -> String
380367 stripLeading _ [] = []
@@ -395,17 +382,13 @@ getCompletions ideOpts CC { allModNamesAsNS, unqualCompls, qualCompls, importabl
395382 = filtModNameCompls ++ map (toggleSnippets caps withSnippets
396383 . mkCompl ideOpts . stripAutoGenerated) filtCompls
397384 ++ filtKeywordCompls
398−
385+
399386 return result
400387
401388-- The supported languages and extensions
402389languagesAndExts :: [T.Text]
403−#if MIN_GHC_API_VERSION(8,10,0)
404−languagesAndExts = map T.pack $ DynFlags.supportedLanguagesAndExtensions ( PlatformMini ArchUnknown OSUnknown )
405−#else
406390languagesAndExts = map T.pack DynFlags.supportedLanguagesAndExtensions
407−#endif
408−
391+
409392-- ---------------------------------------------------------------------
410393-- helper functions for pragmas
411394-- ---------------------------------------------------------------------
@@ -528,3 +511,24 @@ prefixes =
528511 , "$c"
529512 , "$m"
530513 ]
514+
515+keywords :: [T.Text]
516+keywords =
517+ [
518+ -- From https://wiki.haskell.org/Keywords
519+ "as"
520+ , "case", "of"
521+ , "class", "instance", "type"
522+ , "data", "family", "newtype"
523+ , "default"
524+ , "deriving"
525+ , "do", "mdo", "proc", "rec"
526+ , "forall"
527+ , "foreign"
528+ , "hiding"
529+ , "if", "then", "else"
530+ , "import", "qualified", "hiding"
531+ , "infix", "infixl", "infixr"
532+ , "let", "in", "where"
533+ , "module"
534+ ]
src/Development/IDE/Plugin/Completions/Types.hs → src/Development/IDE/Core/CompletionsTypes.hsrenamed+2−2
@@ -1,5 +1,5 @@
1−module Development.IDE.Plugin.Completions.Types (
2− module Development.IDE.Plugin.Completions.Types
1+module Development.IDE.Core.CompletionsTypes (
2+ module Development.IDE.Core.CompletionsTypes
33) where
44
55import Control.DeepSeq
src/Development/IDE/Core/Debouncer.hs+11−18
@@ -3,18 +3,16 @@
33
44module Development.IDE.Core.Debouncer
55 ( Debouncer
6+ , newDebouncer
67 , registerEvent
7− , newAsyncDebouncer
8− , noopDebouncer
98 ) where
109
1110import Control.Concurrent.Extra
1211import Control.Concurrent.Async
1312import Control.Exception
1413import Control.Monad.Extra
15−import Data.Hashable
16−import Data.HashMap.Strict (HashMap)
17−import qualified Data.HashMap.Strict as Map
14+import Data.Map.Strict (Map)
15+import qualified Data.Map.Strict as Map
1816import System.Time.Extra
1917
2018-- | A debouncer can be used to avoid triggering many events
@@ -23,14 +21,13 @@ import System.Time.Extra
2321-- by delaying each event for a given time. If another event
2422-- is registered for the same key within that timeframe,
2523-- only the new event will fire.
26−--
27−-- We abstract over the debouncer used so we an use a proper debouncer in the IDE but disable
28−-- debouncing in the DAML CLI compiler.
29−newtype Debouncer k = Debouncer { registerEvent :: Seconds -> k -> IO () -> IO () }
24+newtype Debouncer k = Debouncer (Var (Map k (Async ())))
3025
31−-- | Debouncer used in the IDE that delays events as expected.
32−newAsyncDebouncer :: (Eq k, Hashable k) => IO (Debouncer k)
33−newAsyncDebouncer = Debouncer . asyncRegisterEvent <$> newVar Map.empty
26+-- | Create a new empty debouncer.
27+newDebouncer :: IO (Debouncer k)
28+newDebouncer = do
29+ m <- newVar Map.empty
30+ pure $ Debouncer m
3431
3532-- | Register an event that will fire after the given delay if no other event
3633-- for the same key gets registered until then.
@@ -38,15 +35,11 @@ newAsyncDebouncer = Debouncer . asyncRegisterEvent <$> newVar Map.empty
3835-- If there is a pending event for the same key, the pending event will be killed.
3936-- Events are run unmasked so it is up to the user of `registerEvent`
4037-- to mask if required.
41−asyncRegisterEvent :: (Eq k, Hashable k) => Var (HashMap k (Async ())) -> Seconds -> k -> IO () -> IO ()
42−asyncRegisterEvent d delay k fire = modifyVar_ d $ \m -> mask_ $ do
38+registerEvent :: Ord k => Debouncer k -> Seconds -> k -> IO () -> IO ()
39+registerEvent (Debouncer d) delay k fire = modifyVar_ d $ \m -> mask_ $ do
4340 whenJust (Map.lookup k m) cancel
4441 a <- asyncWithUnmask $ \unmask -> unmask $ do
4542 sleep delay
4643 fire
4744 modifyVar_ d (pure . Map.delete k)
4845 pure $ Map.insert k a m
49−
50−-- | Debouncer used in the DAML CLI compiler that emits events immediately.
51−noopDebouncer :: Debouncer k
52−noopDebouncer = Debouncer $ \_ _ a -> a
src/Development/IDE/Core/FileExists.hs+46−40
@@ -14,15 +14,13 @@ import Control.Monad.Extra
1414import qualified Data.Aeson as A
1515import Data.Binary
1616import qualified Data.ByteString as BS
17−import Data.HashMap.Strict (HashMap)
18−import qualified Data.HashMap.Strict as HashMap
17+import Data.Map.Strict ( Map )
18+import qualified Data.Map.Strict as Map
1919import Data.Maybe
2020import qualified Data.Text as T
2121import Development.IDE.Core.FileStore
22−import Development.IDE.Core.IdeConfiguration
2322import Development.IDE.Core.Shake
2423import Development.IDE.Types.Location
25−import Development.IDE.Types.Logger
2624import Development.Shake
2725import Development.Shake.Classes
2826import GHC.Generics
@@ -32,7 +30,7 @@ import Language.Haskell.LSP.Types.Capabilities
3230import qualified System.Directory as Dir
3331
3432-- | A map for tracking the file existence
35−type FileExistsMap = (HashMap NormalizedFilePath Bool)
33+type FileExistsMap = (Map NormalizedFilePath Bool)
3634
3735-- | A wrapper around a mutable 'FileExistsMap'
3836newtype FileExistsMapVar = FileExistsMapVar (Var FileExistsMap)
@@ -55,12 +53,12 @@ modifyFileExistsAction f = do
5553modifyFileExists :: IdeState -> [(NormalizedFilePath, Bool)] -> IO ()
5654modifyFileExists state changes = do
5755 FileExistsMapVar var <- getIdeGlobalState state
58− changesMap <- evaluate $ HashMap.fromList changes
56+ changesMap <- evaluate $ Map.fromList changes
5957
6058 -- Masked to ensure that the previous values are flushed together with the map update
6159 mask $ \_ -> do
6260 -- update the map
63− modifyVar_ var $ evaluate . HashMap.union changesMap
61+ modifyVar_ var $ evaluate . Map.union changesMap
6462 -- flush previous values
6563 mapM_ (deleteValue state GetFileExists . fst) changes
6664
@@ -91,34 +89,20 @@ getFileExists fp = use_ GetFileExists fp
9189-- Provides a fast implementation if client supports dynamic watched files.
9290-- Creates a global state as a side effect in that case.
9391fileExistsRules :: IO LspId -> ClientCapabilities -> VFSHandle -> Rules ()
94−fileExistsRules getLspId ClientCapabilities{_workspace} vfs = do
95− -- Create the global always, although it should only be used if we have fast rules.
96− -- But there's a chance someone will send unexpected notifications anyway,
97− -- e.g. https://github.com/digital-asset/ghcide/issues/599
98− addIdeGlobal . FileExistsMapVar =<< liftIO (newVar [])
99− case () of
100− _ | Just WorkspaceClientCapabilities{_didChangeWatchedFiles} <- _workspace
101− , Just DidChangeWatchedFilesClientCapabilities{_dynamicRegistration} <- _didChangeWatchedFiles
102− , Just True <- _dynamicRegistration
103− -> fileExistsRulesFast getLspId vfs
104− | otherwise -> do
105− logger <- logger <$> getShakeExtrasRules
106− liftIO $ logDebug logger "Warning: Client does not support watched files. Falling back to OS polling"
107− fileExistsRulesSlow vfs
92+fileExistsRules getLspId ClientCapabilities{_workspace}
93+ | Just WorkspaceClientCapabilities{_didChangeWatchedFiles} <- _workspace
94+ , Just DidChangeWatchedFilesClientCapabilities{_dynamicRegistration} <- _didChangeWatchedFiles
95+ , Just True <- _dynamicRegistration
96+ = fileExistsRulesFast getLspId
97+ | otherwise = fileExistsRulesSlow
10898
10999-- Requires an lsp client that provides WatchedFiles notifications.
110100fileExistsRulesFast :: IO LspId -> VFSHandle -> Rules ()
111−fileExistsRulesFast getLspId vfs =
101+fileExistsRulesFast getLspId vfs = do
102+ addIdeGlobal . FileExistsMapVar =<< liftIO (newVar [])
112103 defineEarlyCutoff $ \GetFileExists file -> do
113− isWf <- isWorkspaceFile file
114− if isWf
115− then fileExistsFast getLspId vfs file
116− else fileExistsSlow vfs file
117−
118−fileExistsFast :: IO LspId -> VFSHandle -> NormalizedFilePath -> Action (Maybe BS.ByteString, ([a], Maybe Bool))
119−fileExistsFast getLspId vfs file = do
120104 fileExistsMap <- getFileExistsMapUntracked
121− let mbFilesWatched = HashMap.lookup file fileExistsMap
105+ let mbFilesWatched = Map.lookup file fileExistsMap
122106 case mbFilesWatched of
123107 Just fv -> pure (summarizeExists fv, ([], Just fv))
124108 Nothing -> do
@@ -129,7 +113,7 @@ fileExistsFast getLspId vfs file = do
129113 -- taking the FileExistsMap lock to prevent race conditions
130114 -- that would lead to multiple listeners for the same path
131115 modifyFileExistsAction $ \x -> do
132− case HashMap.alterF (,Just exist) file x of
116+ case Map.insertLookupWithKey (\_ x _ -> x) file exist x of
133117 (Nothing, x') -> do
134118 -- if the listener addition fails, we never recover. This is a bug.
135119 addListener eventer file
@@ -150,10 +134,9 @@ fileExistsFast getLspId vfs file = do
150134 WorkspaceDidChangeWatchedFiles
151135 (Just (A.toJSON regOptions))
152136 regOptions =
153− DidChangeWatchedFilesRegistrationOptions { _watchers = List [watcher] }
154− watchKind = WatchKind { _watchCreate = True, _watchChange = False, _watchDelete = True}
155− watcher = FileSystemWatcher { _globPattern = fromNormalizedFilePath fp
156− , _kind = Just watchKind
137+ DidChangeWatchedFilesRegistrationOptions { watchers = List [watcher] }
138+ watcher = FileSystemWatcher { globPattern = fromNormalizedFilePath fp
139+ , kind = Just 5 -- Create and Delete events only
157140 }
158141
159142 eventer $ ReqRegisterCapability req
@@ -162,11 +145,8 @@ summarizeExists :: Bool -> Maybe BS.ByteString
162145summarizeExists x = Just $ if x then BS.singleton 1 else BS.empty
163146
164147fileExistsRulesSlow:: VFSHandle -> Rules ()
165−fileExistsRulesSlow vfs =
166− defineEarlyCutoff $ \GetFileExists file -> fileExistsSlow vfs file
167−
168−fileExistsSlow :: VFSHandle -> NormalizedFilePath -> Action (Maybe BS.ByteString, ([a], Maybe Bool))
169−fileExistsSlow vfs file = do
148+fileExistsRulesSlow vfs = do
149+ defineEarlyCutoff $ \GetFileExists file -> do
170150 alwaysRerun
171151 exist <- liftIO $ getFileExistsVFS vfs file
172152 pure (summarizeExists exist, ([], Just exist))
@@ -179,3 +159,29 @@ getFileExistsVFS vfs file = do
179159 handle (\(_ :: IOException) -> return False) $
180160 (isJust <$> getVirtualFile vfs (filePathToUri' file)) ||^
181161 Dir.doesFileExist (fromNormalizedFilePath file)
162+
163+--------------------------------------------------------------------------------------------------
164+-- The message definitions below probably belong in haskell-lsp-types
165+
166+data DidChangeWatchedFilesRegistrationOptions = DidChangeWatchedFilesRegistrationOptions
167+ { watchers :: List FileSystemWatcher
168+ }
169+
170+instance A.ToJSON DidChangeWatchedFilesRegistrationOptions where
171+ toJSON DidChangeWatchedFilesRegistrationOptions {..} =
172+ A.object ["watchers" A..= watchers]
173+
174+data FileSystemWatcher = FileSystemWatcher
175+ { -- | The glob pattern to watch.
176+ -- For details on glob pattern syntax, check the spec: https://microsoft.github.io/language-server-protocol/specifications/specification-3-14/#workspace_didChangeWatchedFiles
177+ globPattern :: String
178+ -- | The kind of event to subscribe to. Defaults to all.
179+ -- Defined as a bitmap of Create(1), Change(2), and Delete(4)
180+ , kind :: Maybe Int
181+ }
182+
183+instance A.ToJSON FileSystemWatcher where
184+ toJSON FileSystemWatcher {..} =
185+ A.object
186+ $ ["globPattern" A..= globPattern]
187+ ++ [ "kind" A..= x | Just x <- [kind] ]
src/Development/IDE/Core/FileStore.hs+36−13
@@ -11,10 +11,16 @@ module Development.IDE.Core.FileStore(
1111 fileStoreRules,
1212 VFSHandle,
1313 makeVFSHandle,
14− makeLSPVFSHandle
14+ makeLSPVFSHandle,
15+ getSourceFingerprint
1516 ) where
1617
18+import Foreign.Ptr
19+import Foreign.ForeignPtr
20+import Fingerprint
21+import StringBuffer
1722import Development.IDE.GHC.Orphans()
23+import Development.IDE.GHC.Util
1824import Development.IDE.Core.Shake
1925import Control.Concurrent.Extra
2026import qualified Data.Map.Strict as Map
@@ -36,7 +42,6 @@ import qualified Data.Rope.UTF16 as Rope
3642import Data.Time
3743import qualified System.Directory as Dir
3844#else
39−import Foreign.Ptr
4045import Foreign.C.String
4146import Foreign.C.Types
4247import Foreign.Marshal (alloca)
@@ -83,7 +88,9 @@ makeLSPVFSHandle lspFuncs = VFSHandle
8388
8489
8590-- | Get the contents of a file, either dirty (if the buffer is modified) or Nothing to mean use from disk.
86−type instance RuleResult GetFileContents = (FileVersion, Maybe T.Text)
91+type instance RuleResult GetFileContents = (FileVersion, Maybe StringBuffer)
92+
93+type instance RuleResult FingerprintSource = Fingerprint
8794
8895data GetFileContents = GetFileContents
8996 deriving (Eq, Show, Generic)
@@ -91,16 +98,30 @@ instance Hashable GetFileContents
9198instance NFData GetFileContents
9299instance Binary GetFileContents
93100
101+data FingerprintSource = FingerprintSource
102+ deriving (Eq, Show, Generic)
103+instance Hashable FingerprintSource
104+instance NFData FingerprintSource
105+instance Binary FingerprintSource
106+
107+fingerprintSourceRule :: Rules ()
108+fingerprintSourceRule =
109+ define $ \FingerprintSource file -> do
110+ (_, mbContent) <- getFileContents file
111+ content <- liftIO $ maybe (hGetStringBuffer $ fromNormalizedFilePath file) pure mbContent
112+ fingerprint <- liftIO $ fpStringBuffer content
113+ pure ([], Just fingerprint)
114+ where fpStringBuffer (StringBuffer buf len cur) = withForeignPtr buf $ \ptr -> fingerprintData (ptr `plusPtr` cur) len
115+
94116getModificationTimeRule :: VFSHandle -> Rules ()
95117getModificationTimeRule vfs =
96118 defineEarlyCutoff $ \GetModificationTime file -> do
97119 let file' = fromNormalizedFilePath file
98− let wrap time@(l,s) = (Just $ BS.pack $ show time, ([], Just $ ModificationTime l s))
120+ let wrap time = (Just time, ([], Just $ ModificationTime time))
99121 alwaysRerun
100122 mbVirtual <- liftIO $ getVirtualFile vfs $ filePathToUri' file
101123 case mbVirtual of
102− Just (virtualFileVersion -> ver) ->
103− pure (Just $ BS.pack $ show ver, ([], Just $ VFSVersion ver))
124+ Just (virtualFileVersion -> ver) -> pure (Just $ BS.pack $ show ver, ([], Just $ VFSVersion ver))
104125 Nothing -> liftIO $ fmap wrap (getModTime file')
105126 `catch` \(e :: IOException) -> do
106127 let err | isDoesNotExistError e = "File does not exist: " ++ file'
@@ -116,13 +137,11 @@ getModificationTimeRule vfs =
116137 -- We might also want to try speeding this up on Windows at some point.
117138 -- TODO leverage DidChangeWatchedFile lsp notifications on clients that
118139 -- support them, as done for GetFileExists
119− getModTime :: FilePath -> IO (Int,Int)
140+ getModTime :: FilePath -> IO BS.ByteString
120141 getModTime f =
121142#ifdef mingw32_HOST_OS
122143 do time <- Dir.getModificationTime f
123− let !day = fromInteger $ toModifiedJulianDay $ utctDay time
124− !dayTime = fromInteger $ diffTimeToPicoseconds $ utctDayTime time
125− pure (day, dayTime)
144+ pure $! BS.pack $ show (toModifiedJulianDay $ utctDay time, diffTimeToPicoseconds $ utctDayTime time)
126145#else
127146 withCString f $ \f' ->
128147 alloca $ \secPtr ->
@@ -130,13 +149,16 @@ getModificationTimeRule vfs =
130149 Posix.throwErrnoPathIfMinus1Retry_ "getmodtime" f $ c_getModTime f' secPtr nsecPtr
131150 sec <- peek secPtr
132151 nsec <- peek nsecPtr
133− pure (fromEnum sec, fromIntegral nsec)
152+ pure $! BS.pack $ show sec <> "." <> show nsec
134153
135154-- Sadly even unix’s getFileStatus + modificationTimeHiRes is still about twice as slow
136155-- as doing the FFI call ourselves :(.
137156foreign import ccall "getmodtime" c_getModTime :: CString -> Ptr CTime -> Ptr CLong -> IO Int
138157#endif
139158
159+getSourceFingerprint :: NormalizedFilePath -> Action Fingerprint
160+getSourceFingerprint = use_ FingerprintSource
161+
140162getFileContentsRule :: VFSHandle -> Rules ()
141163getFileContentsRule vfs =
142164 define $ \GetFileContents file -> do
@@ -144,7 +166,7 @@ getFileContentsRule vfs =
144166 time <- use_ GetModificationTime file
145167 res <- liftIO $ ideTryIOException file $ do
146168 mbVirtual <- getVirtualFile vfs $ filePathToUri' file
147− pure $ Rope.toText . _text <$> mbVirtual
169+ pure $ textToStringBuffer . Rope.toText . _text <$> mbVirtual
148170 case res of
149171 Left err -> return ([err], Nothing)
150172 Right contents -> return ([], Just (time, contents))
@@ -156,7 +178,7 @@ ideTryIOException fp act =
156178 <$> try act
157179
158180
159−getFileContents :: NormalizedFilePath -> Action (FileVersion, Maybe T.Text)
181+getFileContents :: NormalizedFilePath -> Action (FileVersion, Maybe StringBuffer)
160182getFileContents = use_ GetFileContents
161183
162184fileStoreRules :: VFSHandle -> Rules ()
@@ -164,6 +186,7 @@ fileStoreRules vfs = do
164186 addIdeGlobal vfs
165187 getModificationTimeRule vfs
166188 getFileContentsRule vfs
189+ fingerprintSourceRule
167190
168191
169192-- | Notify the compiler service that a particular file has been modified.
src/Development/IDE/Core/IdeConfiguration.hsdeleted−71
@@ -1,71 +0,0 @@
1−{-# LANGUAGE DuplicateRecordFields #-}
2−module Development.IDE.Core.IdeConfiguration
3− ( IdeConfiguration(..)
4− , registerIdeConfiguration
5− , parseConfiguration
6− , parseWorkspaceFolder
7− , isWorkspaceFile
8− , modifyWorkspaceFolders
9− )
10−where
11−
12−import Control.Concurrent.Extra
13−import Control.Monad
14−import Data.HashSet (HashSet, singleton)
15−import Data.Text (Text, isPrefixOf)
16−import Development.IDE.Core.Shake
17−import Development.IDE.Types.Location
18−import Development.Shake
19−import Language.Haskell.LSP.Types
20−import System.FilePath (isRelative)
21−
22−-- | Lsp client relevant configuration details
23−data IdeConfiguration = IdeConfiguration
24− { workspaceFolders :: HashSet NormalizedUri
25− }
26− deriving (Show)
27−
28−newtype IdeConfigurationVar = IdeConfigurationVar {unIdeConfigurationRef :: Var IdeConfiguration}
29−
30−instance IsIdeGlobal IdeConfigurationVar
31−
32−registerIdeConfiguration :: ShakeExtras -> IdeConfiguration -> IO ()
33−registerIdeConfiguration extras =
34− addIdeGlobalExtras extras . IdeConfigurationVar <=< newVar
35−
36−getIdeConfiguration :: Action IdeConfiguration
37−getIdeConfiguration =
38− getIdeGlobalAction >>= liftIO . readVar . unIdeConfigurationRef
39−
40−parseConfiguration :: InitializeParams -> IdeConfiguration
41−parseConfiguration InitializeParams {..} =
42− IdeConfiguration { .. }
43− where
44− workspaceFolders =
45− foldMap (singleton . toNormalizedUri) _rootUri
46− <> (foldMap . foldMap)
47− (singleton . parseWorkspaceFolder)
48− _workspaceFolders
49−
50−parseWorkspaceFolder :: WorkspaceFolder -> NormalizedUri
51−parseWorkspaceFolder =
52− toNormalizedUri . Uri . (_uri :: WorkspaceFolder -> Text)
53−
54−modifyWorkspaceFolders
55− :: IdeState -> (HashSet NormalizedUri -> HashSet NormalizedUri) -> IO ()
56−modifyWorkspaceFolders ide f = do
57− IdeConfigurationVar var <- getIdeGlobalState ide
58− IdeConfiguration ws <- readVar var
59− writeVar var (IdeConfiguration (f ws))
60−
61−isWorkspaceFile :: NormalizedFilePath -> Action Bool
62−isWorkspaceFile file =
63− if isRelative (fromNormalizedFilePath file)
64− then return True
65− else do
66− IdeConfiguration {..} <- getIdeConfiguration
67− let toText = getUri . fromNormalizedUri
68− return $
69− any
70− (\root -> toText root `isPrefixOf` toText (filePathToUri' file))
71− workspaceFolders
src/Development/IDE/Core/OfInterest.hs+24−24
@@ -1,17 +1,19 @@
11-- Copyright (c) 2019 The DAML Authors. All rights reserved.
22-- SPDX-License-Identifier: Apache-2.0
33
4−{-# LANGUAGE TypeFamilies #-}
4+
5+{-# LANGUAGE TypeFamilies #-}
56{-# LANGUAGE FlexibleInstances #-}
67
7−-- | Utilities and state for the files of interest - those which are currently
8−-- open in the editor. The useful function is 'getFilesOfInterest'.
8+-- | A Shake implementation of the compiler service, built
9+-- using the "Shaker" abstraction layer for in-memory use.
10+--
911module Development.IDE.Core.OfInterest(
1012 ofInterestRules,
1113 getFilesOfInterest, setFilesOfInterest, modifyFilesOfInterest,
1214 ) where
1315
14−import Control.Concurrent.Extra
16+import Control.Concurrent.Extra
1517import Data.Binary
1618import Data.Hashable
1719import Control.DeepSeq
@@ -19,22 +21,25 @@ import GHC.Generics
1921import Data.Typeable
2022import qualified Data.ByteString.UTF8 as BS
2123import Control.Exception
22−import Data.HashSet (HashSet)
23−import qualified Data.HashSet as HashSet
24+import Development.IDE.Types.Location
25+import Development.IDE.Types.Logger
26+import Data.Set (Set)
27+import qualified Data.Set as Set
2428import qualified Data.Text as T
2529import Data.Tuple.Extra
2630import Data.Functor
27−import Development.Shake
31+import Development.Shake
32+
33+import Development.IDE.Core.Shake
2834
29−import Development.IDE.Types.Location
30−import Development.IDE.Types.Logger
31−import Development.IDE.Core.Shake
3235
3336
34−newtype OfInterestVar = OfInterestVar (Var (HashSet NormalizedFilePath))
37+newtype OfInterestVar = OfInterestVar (Var (Set NormalizedFilePath))
3538instance IsIdeGlobal OfInterestVar
3639
37−type instance RuleResult GetFilesOfInterest = HashSet NormalizedFilePath
40+
41+type instance RuleResult GetFilesOfInterest = Set NormalizedFilePath
42+
3843
3944data GetFilesOfInterest = GetFilesOfInterest
4045 deriving (Eq, Show, Typeable, Generic)
@@ -43,18 +48,16 @@ instance NFData GetFilesOfInterest
4348instance Binary GetFilesOfInterest
4449
4550
46−-- | The rule that initialises the files of interest state.
4751ofInterestRules :: Rules ()
4852ofInterestRules = do
49− addIdeGlobal . OfInterestVar =<< liftIO (newVar HashSet.empty)
53+ addIdeGlobal . OfInterestVar =<< liftIO (newVar Set.empty)
5054 defineEarlyCutoff $ \GetFilesOfInterest _file -> assert (null $ fromNormalizedFilePath _file) $ do
5155 alwaysRerun
5256 filesOfInterest <- getFilesOfInterestUntracked
5357 pure (Just $ BS.fromString $ show filesOfInterest, ([], Just filesOfInterest))
5458
5559
56−-- | Get the files that are open in the IDE.
57−getFilesOfInterest :: Action (HashSet NormalizedFilePath)
60+getFilesOfInterest :: Action (Set NormalizedFilePath)
5861getFilesOfInterest = useNoFile_ GetFilesOfInterest
5962
6063
@@ -62,21 +65,18 @@ getFilesOfInterest = useNoFile_ GetFilesOfInterest
6265------------------------------------------------------------
6366-- Exposed API
6467
65−-- | Set the files-of-interest - not usually necessary or advisable.
66−-- The LSP client will keep this information up to date.
67−setFilesOfInterest :: IdeState -> HashSet NormalizedFilePath -> IO ()
68+-- | Set the files-of-interest which will be built and kept-up-to-date.
69+setFilesOfInterest :: IdeState -> Set NormalizedFilePath -> IO ()
6870setFilesOfInterest state files = modifyFilesOfInterest state (const files)
6971
70−getFilesOfInterestUntracked :: Action (HashSet NormalizedFilePath)
72+getFilesOfInterestUntracked :: Action (Set NormalizedFilePath)
7173getFilesOfInterestUntracked = do
7274 OfInterestVar var <- getIdeGlobalAction
7375 liftIO $ readVar var
7476
75−-- | Modify the files-of-interest - not usually necessary or advisable.
76−-- The LSP client will keep this information up to date.
77−modifyFilesOfInterest :: IdeState -> (HashSet NormalizedFilePath -> HashSet NormalizedFilePath) -> IO ()
77+modifyFilesOfInterest :: IdeState -> (Set NormalizedFilePath -> Set NormalizedFilePath) -> IO ()
7878modifyFilesOfInterest state f = do
7979 OfInterestVar var <- getIdeGlobalState state
8080 files <- modifyVar var $ pure . dupe . f
81− logDebug (ideLogger state) $ "Set files of interest to: " <> T.pack (show $ HashSet.toList files)
81+ logDebug (ideLogger state) $ "Set files of interest to: " <> T.pack (show $ Set.toList files)
8282 void $ shakeRun state []
src/Development/IDE/Core/PositionMapping.hs+10−48
@@ -2,15 +2,10 @@
22-- SPDX-License-Identifier: Apache-2.0
33module Development.IDE.Core.PositionMapping
44 ( PositionMapping(..)
5− , fromCurrentPosition
6− , toCurrentPosition
7− , PositionDelta(..)
8− , addDelta
9− , mkDelta
105 , toCurrentRange
116 , fromCurrentRange
127 , applyChange
13− , zeroMapping
8+ , idMapping
149 -- toCurrent and fromCurrent are mainly exposed for testing
1510 , toCurrent
1611 , fromCurrent
@@ -19,25 +14,12 @@ module Development.IDE.Core.PositionMapping
1914import Control.Monad
2015import qualified Data.Text as T
2116import Language.Haskell.LSP.Types
22−import Data.List
2317
24−-- The position delta is the difference between two versions
25−data PositionDelta = PositionDelta
26− { toDelta :: !(Position -> Maybe Position)
27− , fromDelta :: !(Position -> Maybe Position)
18+data PositionMapping = PositionMapping
19+ { toCurrentPosition :: !(Position -> Maybe Position)
20+ , fromCurrentPosition :: !(Position -> Maybe Position)
2821 }
2922
30−fromCurrentPosition :: PositionMapping -> Position -> Maybe Position
31−fromCurrentPosition (PositionMapping pm) = fromDelta pm
32−
33−toCurrentPosition :: PositionMapping -> Position -> Maybe Position
34−toCurrentPosition (PositionMapping pm) = toDelta pm
35−
36−-- A position mapping is the difference from the current version to
37−-- a specific version
38−newtype PositionMapping = PositionMapping PositionDelta
39−
40−
4123toCurrentRange :: PositionMapping -> Range -> Maybe Range
4224toCurrentRange mapping (Range a b) =
4325 Range <$> toCurrentPosition mapping a <*> toCurrentPosition mapping b
@@ -46,33 +28,13 @@ fromCurrentRange :: PositionMapping -> Range -> Maybe Range
4628fromCurrentRange mapping (Range a b) =
4729 Range <$> fromCurrentPosition mapping a <*> fromCurrentPosition mapping b
4830
49−zeroMapping :: PositionMapping
50−zeroMapping = PositionMapping idDelta
51−
52−-- | Compose two position mappings. Composes in the same way as function
53−-- composition (ie the second argument is applyed to the position first).
54−composeDelta :: PositionDelta
55− -> PositionDelta
56− -> PositionDelta
57−composeDelta (PositionDelta to1 from1) (PositionDelta to2 from2) =
58− PositionDelta (to1 <=< to2)
59− (from1 >=> from2)
60−
61−idDelta :: PositionDelta
62−idDelta = PositionDelta Just Just
63−
64−-- | Convert a set of changes into a delta from k to k + 1
65−mkDelta :: [TextDocumentContentChangeEvent] -> PositionDelta
66−mkDelta cs = foldl' applyChange idDelta cs
67−
68−-- | Add a new delta onto a Mapping k n to make a Mapping (k - 1) n
69−addDelta :: PositionDelta -> PositionMapping -> PositionMapping
70−addDelta delta (PositionMapping pm) = PositionMapping (composeDelta delta pm)
31+idMapping :: PositionMapping
32+idMapping = PositionMapping Just Just
7133
72−applyChange :: PositionDelta -> TextDocumentContentChangeEvent -> PositionDelta
73−applyChange PositionDelta{..} (TextDocumentContentChangeEvent (Just r) _ t) = PositionDelta
74− { toDelta = toCurrent r t <=< toDelta
75− , fromDelta = fromDelta <=< fromCurrent r t
34+applyChange :: PositionMapping -> TextDocumentContentChangeEvent -> PositionMapping
35+applyChange posMapping (TextDocumentContentChangeEvent (Just r) _ t) = PositionMapping
36+ { toCurrentPosition = toCurrent r t <=< toCurrentPosition posMapping
37+ , fromCurrentPosition = fromCurrentPosition posMapping <=< fromCurrent r t
7638 }
7739applyChange posMapping _ = posMapping
7840
src/Development/IDE/Core/Preprocessor.hs+6−16
@@ -29,15 +29,11 @@ import Data.IORef (IORef, modifyIORef, newIORef, readIORef)
2929import Data.Text (Text)
3030import qualified Data.Text as T
3131import Outputable (showSDoc)
32−import Control.DeepSeq (NFData(rnf))
33−import Control.Exception (evaluate)
34−import Control.Monad.IO.Class (MonadIO)
35−import Exception (ExceptionMonad)
3632
3733
3834-- | Given a file and some contents, apply any necessary preprocessors,
3935-- e.g. unlit/cpp. Return the resulting buffer and the DynFlags it implies.
40−preprocessor :: (ExceptionMonad m, HasDynFlags m, MonadIO m) => FilePath -> Maybe StringBuffer -> ExceptT [FileDiagnostic] m (StringBuffer, DynFlags)
36+preprocessor :: GhcMonad m => FilePath -> Maybe StringBuffer -> ExceptT [FileDiagnostic] m (StringBuffer, DynFlags)
4137preprocessor filename mbContents = do
4238 -- Perform unlit
4339 (isOnDisk, contents) <-
@@ -100,7 +96,7 @@ data CPPDiag
10096
10197diagsFromCPPLogs :: FilePath -> [CPPLog] -> [FileDiagnostic]
10298diagsFromCPPLogs filename logs =
103− map (\d -> (toNormalizedFilePath' filename, ShowDiag, cppDiagToDiagnostic d)) $
99+ map (\d -> (toNormalizedFilePath filename, ShowDiag, cppDiagToDiagnostic d)) $
104100 go [] logs
105101 where
106102 -- On errors, CPP calls logAction with a real span for the initial log and
@@ -122,8 +118,7 @@ diagsFromCPPLogs filename logs =
122118 _code = Nothing,
123119 _source = Just "CPP",
124120 _message = T.unlines $ cdMessage d,
125− _relatedInformation = Nothing,
126− _tags = Nothing
121+ _relatedInformation = Nothing
127122 }
128123
129124
@@ -133,17 +128,13 @@ isLiterate x = takeExtension x `elem` [".lhs",".lhs-boot"]
133128
134129-- | This reads the pragma information directly from the provided buffer.
135130parsePragmasIntoDynFlags
136− :: (ExceptionMonad m, HasDynFlags m, MonadIO m)
131+ :: GhcMonad m
137132 => FilePath
138133 -> SB.StringBuffer
139134 -> m (Either [FileDiagnostic] DynFlags)
140135parsePragmasIntoDynFlags fp contents = catchSrcErrors "pragmas" $ do
141− dflags0 <- getDynFlags
136+ dflags0 <- getSessionDynFlags
142137 let opts = Hdr.getOptions dflags0 contents fp
143−
144− -- Force bits that might keep the dflags and stringBuffer alive unnecessarily
145− liftIO $ evaluate $ rnf opts
146−
147138 (dflags, _, _) <- parseDynamicFilePragma dflags0 opts
148139 return dflags
149140
@@ -175,12 +166,11 @@ runLhs dflags filename contents = withTempDir $ \dir -> do
175166 escape (c:cs) = c : escape cs
176167 escape [] = []
177168
169+
178170-- | Run CPP on a file
179171runCpp :: DynFlags -> FilePath -> Maybe SB.StringBuffer -> IO SB.StringBuffer
180172runCpp dflags filename contents = withTempDir $ \dir -> do
181173 let out = dir </> takeFileName filename <.> "out"
182− dflags <- pure $ addOptP "-D__GHCIDE__" dflags
183−
184174 case contents of
185175 Nothing -> do
186176 -- Happy case, file is not modified, so run CPP on it in-place
src/Development/IDE/Core/RuleTypes.hs+18−47
@@ -15,6 +15,7 @@ import Control.DeepSeq
1515import Data.Binary
1616import Development.IDE.Import.DependencyInformation
1717import Development.IDE.GHC.Util
18+import Development.IDE.Types.Location
1819import Data.Hashable
1920import Data.Typeable
2021import qualified Data.Set as S
@@ -24,9 +25,10 @@ import GHC.Generics (Generic)
2425import GHC
2526import Module (InstalledUnitId)
2627import HscTypes (CgGuts, Linkable, HomeModInfo, ModDetails)
28+import Development.IDE.GHC.Compat
2729
30+import Development.IDE.Core.CompletionsTypes
2831import Development.IDE.Spans.Type
29−import Development.IDE.Import.FindImports (ArtifactsLocation)
3032
3133
3234-- NOTATION
@@ -57,22 +59,6 @@ instance Show TcModuleResult where
5759instance NFData TcModuleResult where
5860 rnf = rwhnf
5961
60−tmrModSummary :: TcModuleResult -> ModSummary
61−tmrModSummary = pm_mod_summary . tm_parsed_module . tmrModule
62−
63−data HiFileResult = HiFileResult
64− { hirModSummary :: !ModSummary
65− -- Bang patterns here are important to stop the result retaining
66− -- a reference to a typechecked module
67− , hirModIface :: !ModIface
68− }
69−
70−instance NFData HiFileResult where
71− rnf = rwhnf
72−
73−instance Show HiFileResult where
74− show = show . hirModSummary
75−
7662-- | The type checked version of this file, requires TypeCheck+
7763type instance RuleResult TypeCheck = TcModuleResult
7864
@@ -90,24 +76,19 @@ type instance RuleResult GhcSession = HscEnvEq
9076
9177-- | Resolve the imports in a module to the file path of a module
9278-- in the same package or the package id of another package.
93−type instance RuleResult GetLocatedImports = ([(Located ModuleName, Maybe ArtifactsLocation)], S.Set InstalledUnitId)
79+type instance RuleResult GetLocatedImports = ([(Located ModuleName, Maybe NormalizedFilePath)], S.Set InstalledUnitId)
9480
9581-- | This rule is used to report import cycles. It depends on GetDependencyInformation.
9682-- We cannot report the cycles directly from GetDependencyInformation since
9783-- we can only report diagnostics for the current file.
9884type instance RuleResult ReportImportCycles = ()
9985
100−-- | Read the module interface file
101−type instance RuleResult GetHiFile = HiFileResult
86+-- | Read the given HIE file.
87+type instance RuleResult GetHieFile = HieFile
10288
103−-- | Get a module interface, either from an interface file or a typechecked module
104−type instance RuleResult GetModIface = HiFileResult
89+-- | Produce completions info for a file
90+type instance RuleResult ProduceCompletions = (CachedCompletions, TcModuleResult)
10591
106−type instance RuleResult IsFileOfInterest = Bool
107−
108−-- | Generate a ModSummary that has enough information to be used to get .hi and .hie files.
109−-- without needing to parse the entire source
110−type instance RuleResult GetModSummary = ModSummary
11192
11293data GetParsedModule = GetParsedModule
11394 deriving (Eq, Show, Typeable, Generic)
@@ -169,26 +150,16 @@ instance Hashable GhcSession
169150instance NFData GhcSession
170151instance Binary GhcSession
171152
172−data GetHiFile = GetHiFile
173− deriving (Eq, Show, Typeable, Generic)
174−instance Hashable GetHiFile
175−instance NFData GetHiFile
176−instance Binary GetHiFile
177−
178−data GetModIface = GetModIface
179− deriving (Eq, Show, Typeable, Generic)
180−instance Hashable GetModIface
181−instance NFData GetModIface
182−instance Binary GetModIface
183−
184−data IsFileOfInterest = IsFileOfInterest
153+-- Note that we embed the filepath here instead of using the filepath associated with Shake keys.
154+-- Otherwise we will garbage collect the result since files in package dependencies will not be declared reachable.
155+data GetHieFile = GetHieFile FilePath
185156 deriving (Eq, Show, Typeable, Generic)
186−instance Hashable IsFileOfInterest
187−instance NFData IsFileOfInterest
188−instance Binary IsFileOfInterest
157+instance Hashable GetHieFile
158+instance NFData GetHieFile
159+instance Binary GetHieFile
189160
190−data GetModSummary = GetModSummary
161+data ProduceCompletions = ProduceCompletions
191162 deriving (Eq, Show, Typeable, Generic)
192−instance Hashable GetModSummary
193−instance NFData GetModSummary
194−instance Binary GetModSummary
163+instance Hashable ProduceCompletions
164+instance NFData ProduceCompletions
165+instance Binary ProduceCompletions
src/Development/IDE/Core/Rules.hs+136−421
@@ -1,18 +1,16 @@
11-- Copyright (c) 2019 The DAML Authors. All rights reserved.
22-- SPDX-License-Identifier: Apache-2.0
33
4−{-# LANGUAGE CPP #-}
5−{-# LANGUAGE TypeFamilies #-}
6−{-# LANGUAGE FlexibleInstances #-}
4+{-# LANGUAGE TypeFamilies #-}
5+{-# LANGUAGE FlexibleInstances #-}
76{-# LANGUAGE DuplicateRecordFields #-}
8−#include "ghc-api-version.h"
97
108-- | A Shake implementation of the compiler service, built
119-- using the "Shaker" abstraction layer for in-memory use.
1210--
1311module Development.IDE.Core.Rules(
1412 IdeState, GetDependencies(..), GetParsedModule(..), TransitiveDependencies(..),
15− Priority(..), GhcSessionIO(..), GhcSessionFun(..),
13+ Priority(..),
1614 priorityTypeCheck,
1715 priorityGenerateCore,
1816 priorityFilesOfInterest,
@@ -23,59 +21,54 @@ module Development.IDE.Core.Rules(
2321 getDefinition,
2422 getDependencies,
2523 getParsedModule,
24+ fileFromParsedModule,
2625 generateCore,
2726 ) where
2827
2928import Fingerprint
3029
31−import Data.Binary hiding (get, put)
32−import Data.Bifunctor (first, second)
33−import Control.Monad.Extra
30+import Data.Binary
31+import Control.Monad
3432import Control.Monad.Trans.Class
3533import Control.Monad.Trans.Maybe
3634import Development.IDE.Core.Compile
37−import Development.IDE.Core.OfInterest
35+import Development.IDE.Core.Completions
3836import Development.IDE.Types.Options
3937import Development.IDE.Spans.Calculate
4038import Development.IDE.Import.DependencyInformation
4139import Development.IDE.Import.FindImports
4240import Development.IDE.Core.FileExists
43−import Development.IDE.Core.FileStore (getFileContents)
41+import Development.IDE.Core.FileStore (getFileContents, getSourceFingerprint)
4442import Development.IDE.Types.Diagnostics
4543import Development.IDE.Types.Location
46−import Development.IDE.GHC.Compat hiding (parseModule, typecheckModule)
4744import Development.IDE.GHC.Util
48−import Development.IDE.GHC.WithDynFlags
45+import Data.Coerce
4946import Data.Either.Extra
5047import Data.Maybe
5148import Data.Foldable
5249import qualified Data.IntMap.Strict as IntMap
53−import Data.IntMap.Strict (IntMap)
50+import qualified Data.IntSet as IntSet
5451import Data.List
55−import Data.Ord
5652import qualified Data.Set as Set
5753import qualified Data.Text as T
5854import Development.IDE.GHC.Error
5955import Development.Shake hiding (Diagnostic)
6056import Development.IDE.Core.RuleTypes
6157import Development.IDE.Spans.Type
62−import qualified Data.ByteString.Char8 as BS
6358
59+import GHC hiding (parseModule, typecheckModule)
6460import qualified GHC.LanguageExtensions as LangExt
61+import Development.IDE.GHC.Compat (hie_file_result, readHieFile)
62+import UniqSupply
63+import NameCache
6564import HscTypes
66−import PackageConfig
67−import DynFlags (gopt_set, xopt)
65+import DynFlags (xopt)
6866import GHC.Generics(Generic)
6967
7068import qualified Development.IDE.Spans.AtPoint as AtPoint
7169import Development.IDE.Core.Service
7270import Development.IDE.Core.Shake
73−import Development.Shake.Classes hiding (get, put)
74−import Control.Monad.Trans.Except (runExceptT)
75−import Data.ByteString (ByteString)
76−import Control.Concurrent.Async (concurrently)
77−
78−import Control.Monad.State
71+import Development.Shake.Classes
7972
8073-- | This is useful for rules to convert rules that can only produce errors or
8174-- a result into the more general IdeResult type that supports producing
@@ -89,14 +82,14 @@ useE :: IdeRule k v => k -> NormalizedFilePath -> MaybeT Action v
8982useE k = MaybeT . use k
9083
9184useNoFileE :: IdeRule k v => k -> MaybeT Action v
92−useNoFileE k = useE k emptyFilePath
85+useNoFileE k = useE k ""
9386
9487usesE :: IdeRule k v => k -> [NormalizedFilePath] -> MaybeT Action [v]
9588usesE k = MaybeT . fmap sequence . uses k
9689
9790defineNoFile :: IdeRule k v => (k -> Action v) -> Rules ()
9891defineNoFile f = define $ \k file -> do
99− if file == emptyFilePath then do res <- f k; return ([], Just res) else
92+ if file == "" then do res <- f k; return ([], Just res) else
10093 fail $ "Rule " ++ show k ++ " should always be called with the empty string for a file"
10194
10295
@@ -120,58 +113,9 @@ getDefinition :: NormalizedFilePath -> Position -> Action (Maybe Location)
120113getDefinition file pos = fmap join $ runMaybeT $ do
121114 opts <- lift getIdeOptions
122115 spans <- useE GetSpanInfo file
123− lift $ AtPoint.gotoDefinition (getHieFile file) opts (spansExprs spans) pos
124−
125−getHieFile
126− :: NormalizedFilePath -- ^ file we're editing
127− -> Module -- ^ module dep we want info for
128− -> Action (Maybe (HieFile, FilePath)) -- ^ hie stuff for the module
129−getHieFile file mod = do
130− TransitiveDependencies {transitiveNamedModuleDeps} <- use_ GetDependencies file
131− case find (\x -> nmdModuleName x == moduleName mod) transitiveNamedModuleDeps of
132− Just NamedModuleDep{nmdFilePath=nfp} -> do
133− let modPath = fromNormalizedFilePath nfp
134− (_diags, hieFile) <- getHomeHieFile nfp
135− return $ (, modPath) <$> hieFile
136− _ -> getPackageHieFile mod file
137−
138−
139−getHomeHieFile :: NormalizedFilePath -> Action ([a], Maybe HieFile)
140−getHomeHieFile f = do
141− ms <- use_ GetModSummary f
142− let normal_hie_f = toNormalizedFilePath' hie_f
143− hie_f = ml_hie_file $ ms_location ms
144− mbHieTimestamp <- use GetModificationTime normal_hie_f
145− srcTimestamp <- use_ GetModificationTime f
146− let isUpToDate
147− | Just d <- mbHieTimestamp = comparing modificationTime d srcTimestamp == GT
148− | otherwise = False
149−
150− unless isUpToDate $
151− void $ use_ TypeCheck f
152−
153− hf <- liftIO $ whenMaybe isUpToDate (loadHieFile hie_f)
154− return ([], hf)
155−
156−getPackageHieFile :: Module -- ^ Package Module to load .hie file for
157− -> NormalizedFilePath -- ^ Path of home module importing the package module
158− -> Action (Maybe (HieFile, FilePath))
159−getPackageHieFile mod file = do
160− pkgState <- hscEnv <$> use_ GhcSession file
161− IdeOptions {..} <- getIdeOptions
162− let unitId = moduleUnitId mod
163− case lookupPackageConfig unitId pkgState of
164− Just pkgConfig -> do
165− -- 'optLocateHieFile' returns Nothing if the file does not exist
166− hieFile <- liftIO $ optLocateHieFile optPkgLocationOpts pkgConfig mod
167− path <- liftIO $ optLocateSrcFile optPkgLocationOpts pkgConfig mod
168− case (hieFile, path) of
169− (Just hiePath, Just modPath) ->
170− -- deliberately loaded outside the Shake graph
171− -- to avoid dependencies on non-workspace files
172− liftIO $ Just . (, modPath) <$> loadHieFile hiePath
173− _ -> return Nothing
174− _ -> return Nothing
116+ pkgState <- hscEnv <$> useE GhcSession file
117+ let getHieFile x = useNoFile (GetHieFile x)
118+ lift $ AtPoint.gotoDefinition getHieFile opts pkgState (spansExprs spans) pos
175119
176120-- | Parse the contents of a daml file.
177121getParsedModule :: NormalizedFilePath -> Action (Maybe ParsedModule)
@@ -191,60 +135,26 @@ priorityFilesOfInterest :: Priority
191135priorityFilesOfInterest = Priority (-2)
192136
193137getParsedModuleRule :: Rules ()
194−getParsedModuleRule = defineEarlyCutoff $ \GetParsedModule file -> do
195− sess <- use_ GhcSession file
196− let hsc = hscEnv sess
197− -- These packages are used when removing PackageImports from a
198− -- parsed module
199− comp_pkgs = mapMaybe (fmap fst . mkImportDirs (hsc_dflags hsc)) (deps sess)
200− opt <- getIdeOptions
201− (_, contents) <- getFileContents file
202−
203− let dflags = hsc_dflags hsc
204− mainParse = getParsedModuleDefinition hsc opt comp_pkgs file contents
205−
206− -- Parse again (if necessary) to capture Haddock parse errors
207− if gopt Opt_Haddock dflags
208− then
209− liftIO mainParse
210− else do
211− let hscHaddock = hsc{hsc_dflags = gopt_set dflags Opt_Haddock}
212− haddockParse = do
213− (_, (!diagsHaddock, _)) <-
214− getParsedModuleDefinition hscHaddock opt comp_pkgs file contents
215− return diagsHaddock
216−
217− ((fingerPrint, (diags, res)), diagsHaddock) <-
218− -- parse twice, with and without Haddocks, concurrently
219− -- we cannot ignore Haddock parse errors because files of
220− -- non-interest are always parsed with Haddocks
221− liftIO $ concurrently mainParse haddockParse
222−
223− return (fingerPrint, (mergeDiagnostics diags diagsHaddock, res))
224−
225−getParsedModuleDefinition :: HscEnv -> IdeOptions -> [PackageName] -> NormalizedFilePath -> Maybe T.Text -> IO (Maybe ByteString, ([FileDiagnostic], Maybe ParsedModule))
226−getParsedModuleDefinition packageState opt comp_pkgs file contents = do
227− (diag, res) <- parseModule opt packageState comp_pkgs (fromNormalizedFilePath file) (fmap textToStringBuffer contents)
228− case res of
229− Nothing -> pure (Nothing, (diag, Nothing))
230− Just (contents, modu) -> do
231− mbFingerprint <- if isNothing $ optShakeFiles opt
232− then pure Nothing
233− else Just . fingerprintToBS <$> fingerprintFromStringBuffer contents
234− pure (mbFingerprint, (diag, Just modu))
138+getParsedModuleRule =
139+ defineEarlyCutoff $ \GetParsedModule file -> do
140+ (_, contents) <- getFileContents file
141+ packageState <- hscEnv <$> use_ GhcSession file
142+ opt <- getIdeOptions
143+ r <- liftIO $ parseModule opt packageState (fromNormalizedFilePath file) contents
144+ mbFingerprint <- traverse (const $ getSourceFingerprint file) (optShakeFiles opt)
145+ pure (fingerprintToBS <$> mbFingerprint, r)
235146
236147getLocatedImportsRule :: Rules ()
237148getLocatedImportsRule =
238149 define $ \GetLocatedImports file -> do
239− ms <- use_ GetModSummary file
150+ pm <- use_ GetParsedModule file
151+ let ms = pm_mod_summary pm
240152 let imports = [(False, imp) | imp <- ms_textual_imps ms] ++ [(True, imp) | imp <- ms_srcimps ms]
241− env_eq <- use_ GhcSession file
242− let env = hscEnv env_eq
243− let import_dirs = deps env_eq
244− let dflags = addRelativeImport file (moduleName $ ms_mod ms) $ hsc_dflags env
153+ env <- hscEnv <$> use_ GhcSession file
154+ let dflags = addRelativeImport file pm $ hsc_dflags env
245155 opt <- getIdeOptions
246156 (diags, imports') <- fmap unzip $ forM imports $ \(isSource, (mbPkgName, modName)) -> do
247− diagOrImp <- locateModule dflags import_dirs (optExtensions opt) getFileExists modName mbPkgName isSource
157+ diagOrImp <- locateModule dflags (optExtensions opt) getFileExists modName mbPkgName isSource
248158 case diagOrImp of
249159 Left diags -> pure (diags, Left (modName, Nothing))
250160 Right (FileImport path) -> pure ([], Left (modName, Just path))
@@ -258,109 +168,48 @@ getLocatedImportsRule =
258168 Nothing -> pure (concat diags, Nothing)
259169 Just pkgImports -> pure (concat diags, Just (moduleImports, Set.fromList $ concat pkgImports))
260170
261−type RawDepM a = StateT (RawDependencyInformation, IntMap ArtifactsLocation) Action a
262−
263−execRawDepM :: Monad m => StateT (RawDependencyInformation, IntMap a1) m a2 -> m (RawDependencyInformation, IntMap a1)
264−execRawDepM act =
265− execStateT act
266− ( RawDependencyInformation IntMap.empty emptyPathIdMap IntMap.empty
267− , IntMap.empty
268− )
269171
270172-- | Given a target file path, construct the raw dependency results by following
271173-- imports recursively.
272−rawDependencyInformation :: [NormalizedFilePath] -> Action RawDependencyInformation
273−rawDependencyInformation fs = do
274− (rdi, ss) <- execRawDepM (mapM_ go fs)
275− let bm = IntMap.foldrWithKey (updateBootMap rdi) IntMap.empty ss
276− return (rdi { rawBootMap = bm })
174+rawDependencyInformation :: NormalizedFilePath -> Action RawDependencyInformation
175+rawDependencyInformation f = do
176+ let (initialId, initialMap) = getPathId f emptyPathIdMap
177+ go (IntSet.singleton $ getFilePathId initialId)
178+ (RawDependencyInformation IntMap.empty initialMap)
277179 where
278− go :: NormalizedFilePath -- ^ Current module being processed
279− -> StateT (RawDependencyInformation, IntMap ArtifactsLocation) Action FilePathId
280− go f = do
281− -- First check to see if we have already processed the FilePath
282− -- If we have, just return its Id but don't update any of the state.
283− -- Otherwise, we need to process its imports.
284− checkAlreadyProcessed f $ do
285− al <- lift $ modSummaryToArtifactsLocation f <$> use_ GetModSummary f
286− -- Get a fresh FilePathId for the new file
287− fId <- getFreshFid al
288− -- Adding an edge to the bootmap so we can make sure to
289− -- insert boot nodes before the real files.
290− addBootMap al fId
291− -- Try to parse the imports of the file
292− importsOrErr <- lift $ use GetLocatedImports f
293− case importsOrErr of
294− Nothing -> do
295− -- File doesn't parse so add the module as a failure into the
296− -- dependency information, continue processing the other
297− -- elements in the queue
298− modifyRawDepInfo (insertImport fId (Left ModuleParseError))
299− return fId
300− Just (modImports, pkgImports) -> do
301− -- Get NFPs of the imports which have corresponding files
302− -- Imports either come locally from a file or from a package.
303− let (no_file, with_file) = splitImports modImports
304− (mns, ls) = unzip with_file
305− -- Recursively process all the imports we just learnt about
306− -- and get back a list of their FilePathIds
307− fids <- mapM (go . artifactFilePath) ls
308− -- Associate together the ModuleName with the FilePathId
309− let moduleImports' = map (,Nothing) no_file ++ zip mns (map Just fids)
310− -- Insert into the map the information about this modules
311− -- imports.
312− modifyRawDepInfo $ insertImport fId (Right $ ModuleImports moduleImports' pkgImports)
313− return fId
314−
315−
316− checkAlreadyProcessed :: NormalizedFilePath -> RawDepM FilePathId -> RawDepM FilePathId
317− checkAlreadyProcessed nfp k = do
318− (rawDepInfo, _) <- get
319− maybe k return (lookupPathToId (rawPathIdMap rawDepInfo) nfp)
320−
321− modifyRawDepInfo :: (RawDependencyInformation -> RawDependencyInformation) -> RawDepM ()
322− modifyRawDepInfo f = modify (first f)
323−
324− addBootMap :: ArtifactsLocation -> FilePathId -> RawDepM ()
325− addBootMap al fId =
326− modify (\(rd, ss) -> (rd, if isBootLocation al
327− then IntMap.insert (getFilePathId fId) al ss
328− else ss))
329−
330− getFreshFid :: ArtifactsLocation -> RawDepM FilePathId
331− getFreshFid al = do
332− (rawDepInfo, ss) <- get
333− let (fId, path_map) = getPathId al (rawPathIdMap rawDepInfo)
334− -- Insert the File into the bootmap if it's a boot module
335− let rawDepInfo' = rawDepInfo { rawPathIdMap = path_map }
336− put (rawDepInfo', ss)
337− return fId
338−
339− -- Split in (package imports, local imports)
340− splitImports :: [(Located ModuleName, Maybe ArtifactsLocation)]
341− -> ([Located ModuleName], [(Located ModuleName, ArtifactsLocation)])
342− splitImports = foldr splitImportsLoop ([],[])
343−
344− splitImportsLoop (imp, Nothing) (ns, ls) = (imp:ns, ls)
345− splitImportsLoop (imp, Just artifact) (ns, ls) = (ns, (imp,artifact) : ls)
346−
347− updateBootMap pm boot_mod_id ArtifactsLocation{..} bm =
348− if not artifactIsSource
349− then
350− let msource_mod_id = lookupPathToId (rawPathIdMap pm) (toNormalizedFilePath' $ dropBootSuffix artifactModLocation)
351− in case msource_mod_id of
352− Just source_mod_id -> insertBootId source_mod_id (FilePathId boot_mod_id) bm
353− Nothing -> bm
354− else bm
355−
356− dropBootSuffix :: ModLocation -> FilePath
357− dropBootSuffix (ModLocation (Just hs_src) _ _) = reverse . drop (length @[] "-boot") . reverse $ hs_src
358− dropBootSuffix _ = error "dropBootSuffix"
180+ go fs rawDepInfo =
181+ case IntSet.minView fs of
182+ -- Queue is empty
183+ Nothing -> pure rawDepInfo
184+ -- Pop f from the queue and process it
185+ Just (f, fs) -> do
186+ let fId = FilePathId f
187+ importsOrErr <- use GetLocatedImports $ idToPath (rawPathIdMap rawDepInfo) fId
188+ case importsOrErr of
189+ Nothing ->
190+ -- File doesn’t parse
191+ let rawDepInfo' = insertImport fId (Left ModuleParseError) rawDepInfo
192+ in go fs rawDepInfo'
193+ Just (modImports, pkgImports) -> do
194+ let f :: PathIdMap -> (a, Maybe NormalizedFilePath) -> (PathIdMap, (a, Maybe FilePathId))
195+ f pathMap (imp, mbPath) = case mbPath of
196+ Nothing -> (pathMap, (imp, Nothing))
197+ Just path ->
198+ let (pathId, pathMap') = getPathId path pathMap
199+ in (pathMap', (imp, Just pathId))
200+ -- Convert paths in imports to ids and update the path map
201+ let (pathIdMap, modImports') = mapAccumL f (rawPathIdMap rawDepInfo) modImports
202+ -- Files that we haven’t seen before are added to the queue.
203+ let newFiles =
204+ IntSet.fromList (coerce $ mapMaybe snd modImports')
205+ IntSet.\\ IntMap.keysSet (rawImports rawDepInfo)
206+ let rawDepInfo' = insertImport fId (Right $ ModuleImports modImports' pkgImports) rawDepInfo
207+ go (newFiles `IntSet.union` fs) (rawDepInfo' { rawPathIdMap = pathIdMap })
359208
360209getDependencyInformationRule :: Rules ()
361210getDependencyInformationRule =
362211 define $ \GetDependencyInformation file -> do
363− rawDepInfo <- rawDependencyInformation [file]
212+ rawDepInfo <- rawDependencyInformation file
364213 pure ([], Just $ processDependencyInformation rawDepInfo)
365214
366215reportImportCyclesRule :: Rules ()
@@ -388,13 +237,12 @@ reportImportCyclesRule =
388237 , _message = "Cyclic module dependency between " <> showCycle mods
389238 , _code = Nothing
390239 , _relatedInformation = Nothing
391− , _tags = Nothing
392240 }
393241 where loc = srcSpanToLocation (getLoc imp)
394− fp = toNormalizedFilePath' $ srcSpanToFilename (getLoc imp)
242+ fp = toNormalizedFilePath $ srcSpanToFilename (getLoc imp)
395243 getModuleName file = do
396− ms <- use_ GetModSummary file
397− pure (moduleNameString . moduleName . ms_mod $ ms)
244+ pm <- use_ GetParsedModule file
245+ pure (moduleNameString . moduleName . ms_mod $ pm_mod_summary pm)
398246 showCycle mods = T.intercalate ", " (map T.pack mods)
399247
400248-- returns all transitive dependencies in topological order.
@@ -402,7 +250,7 @@ reportImportCyclesRule =
402250getDependenciesRule :: Rules ()
403251getDependenciesRule =
404252 defineEarlyCutoff $ \GetDependencies file -> do
405− depInfo <- use_ GetDependencyInformation file
253+ depInfo@DependencyInformation{..} <- use_ GetDependencyInformation file
406254 let allFiles = reachableModules depInfo
407255 _ <- uses_ ReportImportCycles allFiles
408256 opts <- getIdeOptions
@@ -414,101 +262,64 @@ getSpanInfoRule :: Rules ()
414262getSpanInfoRule =
415263 define $ \GetSpanInfo file -> do
416264 tc <- use_ TypeCheck file
417− packageState <- hscEnv <$> use_ GhcSession file
418− deps <- maybe (TransitiveDependencies [] [] []) fst <$> useWithStale GetDependencies file
419− let tdeps = transitiveModuleDeps deps
420−
421−-- When possible, rely on the haddocks embedded in our interface files
422−-- This creates problems on ghc-lib, see comment on 'getDocumentationTryGhc'
423−#if MIN_GHC_API_VERSION(8,6,0) && !defined(GHC_LIB)
424− let parsedDeps = []
425−#else
426− parsedDeps <- uses_ GetParsedModule tdeps
427−#endif
428−
429− ifaces <- uses_ GetModIface tdeps
265+ deps <- maybe (TransitiveDependencies [] []) fst <$> useWithStale GetDependencies file
266+ tms <- mapMaybe (fmap fst) <$> usesWithStale TypeCheck (transitiveModuleDeps deps)
430267 (fileImports, _) <- use_ GetLocatedImports file
431− let imports = second (fmap artifactFilePath) <$> fileImports
432− x <- liftIO $ getSrcSpanInfos packageState imports tc parsedDeps (map hirModIface ifaces)
268+ packageState <- hscEnv <$> use_ GhcSession file
269+ x <- liftIO $ getSrcSpanInfos packageState fileImports tc tms
433270 return ([], Just x)
434271
435272-- Typechecks a module.
436273typeCheckRule :: Rules ()
437−typeCheckRule = define $ \TypeCheck file -> do
438− pm <- use_ GetParsedModule file
439− -- do not generate interface files as this rule is called
440− -- for files of interest on every keystroke
441− typeCheckRuleDefinition file pm SkipGenerationOfInterfaceFiles
442−
443−data GenerateInterfaceFiles
444− = DoGenerateInterfaceFiles
445− | SkipGenerationOfInterfaceFiles
446− deriving (Show)
447−
448−-- This is factored out so it can be directly called from the GetModIface
449−-- rule. Directly calling this rule means that on the initial load we can
450−-- garbage collect all the intermediate typechecked modules rather than
451−-- retain the information forever in the shake graph.
452−typeCheckRuleDefinition
453− :: NormalizedFilePath -- ^ Path to source file
454− -> ParsedModule
455− -> GenerateInterfaceFiles -- ^ Should generate .hi and .hie files ?
456− -> Action (IdeResult TcModuleResult)
457−typeCheckRuleDefinition file pm generateArtifacts = do
458− deps <- use_ GetDependencies file
459− hsc <- hscEnv <$> use_ GhcSession file
460− -- Figure out whether we need TemplateHaskell or QuasiQuotes support
461− let graph_needs_th_qq = needsTemplateHaskellOrQQ $ hsc_mod_graph hsc
462− file_uses_th_qq = uses_th_qq $ ms_hspp_opts (pm_mod_summary pm)
463− any_uses_th_qq = graph_needs_th_qq || file_uses_th_qq
464− mirs <- uses_ GetModIface (transitiveModuleDeps deps)
465− bytecodes <- if any_uses_th_qq
466− then -- If we use TH or QQ, we must obtain the bytecode
467− fmap Just <$> uses_ GenerateByteCode (transitiveModuleDeps deps)
468− else
469− pure $ repeat Nothing
470−
471− setPriority priorityTypeCheck
472− IdeOptions { optDefer = defer } <- getIdeOptions
473−
474− addUsageDependencies $ liftIO $ do
475− res <- typecheckModule defer hsc (zipWith unpack mirs bytecodes) pm
476− case res of
477− (diags, Just (hsc,tcm)) | DoGenerateInterfaceFiles <- generateArtifacts -> do
478− diagsHie <- generateAndWriteHieFile hsc (tmrModule tcm)
479− diagsHi <- generateAndWriteHiFile hsc tcm
480− return (diags <> diagsHi <> diagsHie, Just tcm)
481− (diags, res) ->
482− return (diags, snd <$> res)
483− where
484− unpack HiFileResult{..} bc = (hirModSummary, (hirModIface, bc))
485− uses_th_qq dflags =
486− xopt LangExt.TemplateHaskell dflags || xopt LangExt.QuasiQuotes dflags
487−
488− addUsageDependencies :: Action (a, Maybe TcModuleResult) -> Action (a, Maybe TcModuleResult)
489− addUsageDependencies a = do
490− r@(_, mtc) <- a
491− forM_ mtc $ \tc -> do
492− let used_files = mapMaybe udep (mi_usages (hm_iface (tmrModInfo tc)))
493− udep (UsageFile fp _h) = Just fp
494− udep _ = Nothing
495− -- Add a dependency on these files which are added by things like
496− -- qAddDependentFile
497− void $ uses_ GetModificationTime (map toNormalizedFilePath' used_files)
498− return r
499−
500−
501−generateCore :: RunSimplifier -> NormalizedFilePath -> Action (IdeResult (SafeHaskellMode, CgGuts, ModDetails))
502−generateCore runSimplifier file = do
274+typeCheckRule =
275+ define $ \TypeCheck file -> do
276+ pm <- use_ GetParsedModule file
277+ deps <- use_ GetDependencies file
278+ packageState <- hscEnv <$> use_ GhcSession file
279+ -- Figure out whether we need TemplateHaskell or QuasiQuotes support
280+ let graph_needs_th_qq = needsTemplateHaskellOrQQ $ hsc_mod_graph packageState
281+ file_uses_th_qq = uses_th_qq $ ms_hspp_opts (pm_mod_summary pm)
282+ any_uses_th_qq = graph_needs_th_qq || file_uses_th_qq
283+ tms <- if any_uses_th_qq
284+ -- If we use TH or QQ, we must obtain the bytecode
285+ then do
286+ bytecodes <- uses_ GenerateByteCode (transitiveModuleDeps deps)
287+ tmrs <- uses_ TypeCheck (transitiveModuleDeps deps)
288+ pure (zipWith addByteCode bytecodes tmrs)
289+ else uses_ TypeCheck (transitiveModuleDeps deps)
290+ setPriority priorityTypeCheck
291+ IdeOptions{ optDefer = defer} <- getIdeOptions
292+ liftIO $ typecheckModule defer packageState tms pm
293+ where
294+ uses_th_qq dflags = xopt LangExt.TemplateHaskell dflags || xopt LangExt.QuasiQuotes dflags
295+ addByteCode :: Linkable -> TcModuleResult -> TcModuleResult
296+ addByteCode lm tmr = tmr { tmrModInfo = (tmrModInfo tmr) { hm_linkable = Just lm } }
297+
298+generateCore :: NormalizedFilePath -> Action (IdeResult (SafeHaskellMode, CgGuts, ModDetails))
299+generateCore file = do
503300 deps <- use_ GetDependencies file
504301 (tm:tms) <- uses_ TypeCheck (file:transitiveModuleDeps deps)
505302 setPriority priorityGenerateCore
506303 packageState <- hscEnv <$> use_ GhcSession file
507− liftIO $ compileModule runSimplifier packageState [(tmrModSummary x, tmrModInfo x) | x <- tms] tm
304+ liftIO $ compileModule packageState tms tm
508305
509306generateCoreRule :: Rules ()
510307generateCoreRule =
511− define $ \GenerateCore -> generateCore (RunSimplifier True)
308+ define $ \GenerateCore -> generateCore
309+
310+produceCompletions :: Rules ()
311+produceCompletions =
312+ define $ \ProduceCompletions file -> do
313+ deps <- maybe (TransitiveDependencies [] []) fst <$> useWithStale GetDependencies file
314+ tms <- mapMaybe (fmap fst) <$> usesWithStale TypeCheck (transitiveModuleDeps deps)
315+ tm <- fmap fst <$> useWithStale TypeCheck file
316+ packageState <- fmap (hscEnv . fst) <$> useWithStale GhcSession file
317+ case (tm, packageState) of
318+ (Just tm', Just packageState') -> do
319+ cdata <- liftIO $ cacheDataProducer packageState' (hsc_dflags packageState')
320+ (tmrModule tm') (map tmrModule tms)
321+ return ([], Just (cdata, tm'))
322+ _ -> return ([], Nothing)
512323
513324generateByteCodeRule :: Rules ()
514325generateByteCodeRule =
@@ -517,7 +328,7 @@ generateByteCodeRule =
517328 (tm : tms) <- uses_ TypeCheck (file: transitiveModuleDeps deps)
518329 session <- hscEnv <$> use_ GhcSession file
519330 (_, guts, _) <- use_ GenerateCore file
520− liftIO $ generateByteCode session [(tmrModSummary x, tmrModInfo x) | x <- tms] tm guts
331+ liftIO $ generateByteCode session tms tm guts
521332
522333-- A local rule type to get caching. We want to use newCache, but it has
523334-- thread killed exception issues, so we lift it to a full rule.
@@ -529,7 +340,7 @@ instance Hashable GhcSessionIO
529340instance NFData GhcSessionIO
530341instance Binary GhcSessionIO
531342
532−newtype GhcSessionFun = GhcSessionFun (FilePath -> Action (IdeResult HscEnvEq))
343+newtype GhcSessionFun = GhcSessionFun (FilePath -> Action HscEnvEq)
533344instance Show GhcSessionFun where show _ = "GhcSessionFun"
534345instance NFData GhcSessionFun where rnf !_ = ()
535346
@@ -538,119 +349,20 @@ loadGhcSession :: Rules ()
538349loadGhcSession = do
539350 defineNoFile $ \GhcSessionIO -> do
540351 opts <- getIdeOptions
541− GhcSessionFun <$> optGhcSession opts
542− -- This function should always be rerun because it consults a cache to
543− -- see what HscEnv needs to be used for the file, which can change.
544− -- However, it should also cut-off early if it's the same HscEnv as
545− -- last time
352+ liftIO $ GhcSessionFun <$> optGhcSession opts
546353 defineEarlyCutoff $ \GhcSession file -> do
547354 GhcSessionFun fun <- useNoFile_ GhcSessionIO
548− alwaysRerun
549355 val <- fun $ fromNormalizedFilePath file
550−
551− -- TODO: What was this doing before?
552356 opts <- getIdeOptions
553− let cutoffHash =
554− case optShakeFiles opts of
555− -- optShakeFiles is only set in the DAML case.
556− -- https://github.com/digital-asset/ghcide/pull/522#discussion_r428622915
557− Just {} -> ""
558− -- Hash the HscEnvEq returned so cutoff if it didn't change
559− -- from last time
560− Nothing -> BS.pack (show (hash (snd val)))
561− return (Just cutoffHash, val)
562−
563−getHiFileRule :: Rules ()
564−getHiFileRule = defineEarlyCutoff $ \GetHiFile f -> do
565− -- get all dependencies interface files, to check for freshness
566− (deps,_) <- use_ GetLocatedImports f
567− depHis <- traverse (use GetHiFile) (mapMaybe (fmap artifactFilePath . snd) deps)
568−
569− ms <- use_ GetModSummary f
570− let hiFile = toNormalizedFilePath'
571− $ case ms_hsc_src ms of
572− HsBootFile -> addBootSuffix (ml_hi_file $ ms_location ms)
573− _ -> ml_hi_file $ ms_location ms
574−
575− case sequence depHis of
576− Nothing -> pure (Nothing, ([], Nothing))
577− Just deps -> do
578− gotHiFile <- getFileExists hiFile
579− if not gotHiFile
580− then pure (Nothing, ([], Nothing))
581− else do
582− hiVersion <- use_ GetModificationTime hiFile
583− modVersion <- use_ GetModificationTime f
584− let sourceModified = modificationTime hiVersion < modificationTime modVersion
585− if sourceModified
586− then do
587− pure (Nothing, ([], Nothing))
588− else do
589− session <- hscEnv <$> use_ GhcSession f
590− r <- liftIO $ loadInterface session ms deps
591− case r of
592− Right iface -> do
593− let result = HiFileResult ms iface
594− return (Just (fingerprintToBS (getModuleHash iface)), ([], Just result))
595− Left err -> do
596− let diag = ideErrorWithSource (Just "interface file loading") (Just DsError) f . T.pack $ err
597− return (Nothing, (pure diag, Nothing))
598−
599−getModSummaryRule :: Rules ()
600−getModSummaryRule = define $ \GetModSummary f -> do
601− dflags <- hsc_dflags . hscEnv <$> use_ GhcSession f
602− (_, mFileContent) <- getFileContents f
603− modS <- liftIO $ evalWithDynFlags dflags $ runExceptT $
604− getModSummaryFromImports (fromNormalizedFilePath f) (textToStringBuffer <$> mFileContent)
605− return $ either (,Nothing) (([], ) . Just) modS
606−
607−getModIfaceRule :: Rules ()
608−getModIfaceRule = define $ \GetModIface f -> do
609− fileOfInterest <- use_ IsFileOfInterest f
610− let useHiFile =
611− -- Never load interface files for files of interest
612− not fileOfInterest
613− mbHiFile <- if useHiFile then use GetHiFile f else return Nothing
614− case mbHiFile of
615− Just x ->
616− return ([], Just x)
617− Nothing
618− | fileOfInterest -> do
619− -- For files of interest only, create a Shake dependency on typecheck
620− tmr <- use TypeCheck f
621− return ([], extract tmr)
622− | otherwise -> do
623− -- the interface file does not exist or is out of date.
624− -- Invoke typechecking directly to update it without incurring a dependency
625− -- on the parsed module and the typecheck rules
626− sess <- use_ GhcSession f
627− let hsc = hscEnv sess
628− -- After parsing the module remove all package imports referring to
629− -- these packages as we have already dealt with what they map to.
630− comp_pkgs = mapMaybe (fmap fst . mkImportDirs (hsc_dflags hsc)) (deps sess)
631− opt <- getIdeOptions
632− (_, contents) <- getFileContents f
633− -- Embed --haddocks in the interface file
634− hsc <- pure hsc{hsc_dflags = gopt_set (hsc_dflags hsc) Opt_Haddock}
635− (_, (diags, mb_pm)) <- liftIO $ getParsedModuleDefinition hsc opt comp_pkgs f contents
636− case mb_pm of
637− Nothing -> return (diags, Nothing)
638− Just pm -> do
639− (diags', tmr) <- typeCheckRuleDefinition f pm DoGenerateInterfaceFiles
640− -- Bang pattern is important to avoid leaking 'tmr'
641− let !res = extract tmr
642− return (diags <> diags', res)
643− where
644− extract Nothing = Nothing
645− extract (Just tmr) =
646− -- Bang patterns are important to force the inner fields
647− Just $! HiFileResult (tmrModSummary tmr) (hm_iface $ tmrModInfo tmr)
357+ return ("" <$ optShakeFiles opts, ([], Just val))
358+
648359
649−isFileOfInterestRule :: Rules ()
650−isFileOfInterestRule = defineEarlyCutoff $ \IsFileOfInterest f -> do
651− filesOfInterest <- getFilesOfInterest
652− let res = f `elem` filesOfInterest
653− return (Just (if res then "1" else ""), ([], Just res))
360+getHieFileRule :: Rules ()
361+getHieFileRule =
362+ defineNoFile $ \(GetHieFile f) -> do
363+ u <- liftIO $ mkSplitUniqSupply 'a'
364+ let nameCache = initNameCache u []
365+ liftIO $ fmap (hie_file_result . fst) $ readHieFile nameCache f
654366
655367-- | A rule that wires per-file rules together
656368mainRule :: Rules ()
@@ -665,7 +377,10 @@ mainRule = do
665377 generateCoreRule
666378 generateByteCodeRule
667379 loadGhcSession
668− getHiFileRule
669− getModIfaceRule
670− isFileOfInterestRule
671− getModSummaryRule
380+ getHieFileRule
381+ produceCompletions
382+
383+------------------------------------------------------------
384+
385+fileFromParsedModule :: ParsedModule -> NormalizedFilePath
386+fileFromParsedModule = toNormalizedFilePath . ms_hspp_file . pm_mod_summary
src/Development/IDE/Core/Service.hs+1−4
@@ -23,7 +23,6 @@ import Control.Concurrent.Async
2323import Data.Maybe
2424import Development.IDE.Types.Options (IdeOptions(..))
2525import Control.Monad
26−import Development.IDE.Core.Debouncer
2726import Development.IDE.Core.FileStore (VFSHandle, fileStoreRules)
2827import Development.IDE.Core.FileExists (fileExistsRules)
2928import Development.IDE.Core.OfInterest
@@ -50,16 +49,14 @@ initialise :: LSP.ClientCapabilities
5049 -> IO LSP.LspId
5150 -> (LSP.FromServerMessage -> IO ())
5251 -> Logger
53− -> Debouncer LSP.NormalizedUri
5452 -> IdeOptions
5553 -> VFSHandle
5654 -> IO IdeState
57−initialise caps mainRule getLspId toDiags logger debouncer options vfs =
55+initialise caps mainRule getLspId toDiags logger options vfs =
5856 shakeOpen
5957 getLspId
6058 toDiags
6159 logger
62− debouncer
6360 (optShakeProfiling options)
6461 (optReportProgress options)
6562 shakeOptions
src/Development/IDE/Core/Shake.hs+127−148
@@ -19,24 +19,24 @@
1919-- always stored as real Haskell values, whereas Shake serialises all 'A' values
2020-- between runs. To deserialise a Shake value, we just consult Values.
2121module Development.IDE.Core.Shake(
22− IdeState, shakeExtras,
23− ShakeExtras(..), getShakeExtras, getShakeExtrasRules,
22+ IdeState,
23+ ShakeExtras(..), getShakeExtras,
2424 IdeRule, IdeResult, GetModificationTime(..),
2525 shakeOpen, shakeShut,
2626 shakeRun,
2727 shakeProfile,
2828 use, useWithStale, useNoFile, uses, usesWithStale,
2929 use_, useNoFile_, uses_,
30− define, defineEarlyCutoff, defineOnDisk, needOnDisk, needOnDisks,
30+ define, defineEarlyCutoff, defineOnDisk, needOnDisk, needOnDisks, fingerprintToBS,
3131 getDiagnostics, unsafeClearDiagnostics,
3232 getHiddenDiagnostics,
33− IsIdeGlobal, addIdeGlobal, addIdeGlobalExtras, getIdeGlobalState, getIdeGlobalAction,
33+ IsIdeGlobal, addIdeGlobal, getIdeGlobalState, getIdeGlobalAction,
3434 garbageCollect,
3535 setPriority,
3636 sendEvent,
3737 ideLogger,
3838 actionLogger,
39− FileVersion(..), modificationTime,
39+ FileVersion(..),
4040 Priority(..),
4141 updatePositionMapping,
4242 deleteValue,
@@ -49,11 +49,13 @@ import Development.Shake.Classes
4949import Development.Shake.Rule
5050import qualified Data.HashMap.Strict as HMap
5151import qualified Data.Map.Strict as Map
52+import qualified Data.Map.Merge.Strict as Map
5253import qualified Data.ByteString.Char8 as BS
54+import qualified Data.ByteString.Internal as BS
5355import Data.Dynamic
5456import Data.Maybe
5557import Data.Map.Strict (Map)
56−import Data.List.Extra (partition, takeEnd)
58+import Data.List.Extra (foldl', partition, takeEnd)
5759import qualified Data.Set as Set
5860import qualified Data.Text as T
5961import Data.Traversable (for)
@@ -62,6 +64,9 @@ import Data.Unique
6264import Development.IDE.Core.Debouncer
6365import Development.IDE.Core.PositionMapping
6466import Development.IDE.Types.Logger hiding (Priority)
67+import Foreign.Ptr
68+import Foreign.Storable
69+import GHC.Fingerprint
6570import Language.Haskell.LSP.Diagnostics
6671import qualified Data.SortedList as SL
6772import Development.IDE.Types.Diagnostics
@@ -69,7 +74,7 @@ import Development.IDE.Types.Location
6974import Development.IDE.Types.Options
7075import Control.Concurrent.Async
7176import Control.Concurrent.Extra
72−import Control.Exception.Extra
77+import Control.Exception
7378import Control.DeepSeq
7479import System.Time.Extra
7580import Data.Typeable
@@ -94,16 +99,12 @@ data ShakeExtras = ShakeExtras
9499 ,state :: Var Values
95100 ,diagnostics :: Var DiagnosticStore
96101 ,hiddenDiagnostics :: Var DiagnosticStore
97− ,publishedDiagnostics :: Var (HMap.HashMap NormalizedUri [Diagnostic])
102+ ,publishedDiagnostics :: Var (Map NormalizedUri [Diagnostic])
98103 -- ^ This represents the set of diagnostics that we have published.
99104 -- Due to debouncing not every change might get published.
100− ,positionMapping :: Var (HMap.HashMap NormalizedUri (Map TextDocumentVersion (PositionDelta, PositionMapping)))
105+ ,positionMapping :: Var (Map NormalizedUri (Map TextDocumentVersion PositionMapping))
101106 -- ^ Map from a text document version to a PositionMapping that describes how to map
102107 -- positions in a version of that document to positions in the latest version
103− -- First mapping is delta from previous version and second one is an
104− -- accumlation of all previous mappings.
105− ,inProgress :: Var (HMap.HashMap NormalizedFilePath Int)
106− -- ^ How many rules are running for each file
107108 }
108109
109110getShakeExtras :: Action ShakeExtras
@@ -116,29 +117,22 @@ getShakeExtrasRules = do
116117 Just x <- getShakeExtraRules @ShakeExtras
117118 return x
118119
120+
121+
119122class Typeable a => IsIdeGlobal a where
120123
121124addIdeGlobal :: IsIdeGlobal a => a -> Rules ()
122−addIdeGlobal x = do
123− extras <- getShakeExtrasRules
124− liftIO $ addIdeGlobalExtras extras x
125−
126−addIdeGlobalExtras :: IsIdeGlobal a => ShakeExtras -> a -> IO ()
127−addIdeGlobalExtras ShakeExtras{globals} x@(typeOf -> ty) =
125+addIdeGlobal x@(typeOf -> ty) = do
126+ ShakeExtras{globals} <- getShakeExtrasRules
128127 liftIO $ modifyVar_ globals $ \mp -> case HMap.lookup ty mp of
129− Just _ -> errorIO $ "Internal error, addIdeGlobalExtras, got the same type twice for " ++ show ty
128+ Just _ -> error $ "Can't addIdeGlobal twice on the same type, got " ++ show ty
130129 Nothing -> return $! HMap.insert ty (toDyn x) mp
131130
132131
133132getIdeGlobalExtras :: forall a . IsIdeGlobal a => ShakeExtras -> IO a
134133getIdeGlobalExtras ShakeExtras{globals} = do
135− let typ = typeRep (Proxy :: Proxy a)
136− x <- HMap.lookup (typeRep (Proxy :: Proxy a)) <$> readVar globals
137− case x of
138− Just x
139− | Just x <- fromDynamic x -> pure x
140− | otherwise -> errorIO $ "Internal error, getIdeGlobalExtras, wrong type for " ++ show typ ++ " (got " ++ show (dynTypeRep x) ++ ")"
141− Nothing -> errorIO $ "Internal error, getIdeGlobalExtras, no entry for " ++ show typ
134+ Just x <- HMap.lookup (typeRep (Proxy :: Proxy a)) <$> readVar globals
135+ return $ fromDyn x $ error "Serious error, corrupt globals"
142136
143137getIdeGlobalAction :: forall a . IsIdeGlobal a => Action a
144138getIdeGlobalAction = liftIO . getIdeGlobalExtras =<< getShakeExtras
@@ -161,7 +155,7 @@ instance Eq Key where
161155 | otherwise = False
162156
163157instance Hashable Key where
164− hashWithSalt salt (Key key) = hashWithSalt salt (typeOf key, key)
158+ hashWithSalt salt (Key key) = hashWithSalt salt key
165159
166160-- | The result of an IDE operation. Warnings and errors are in the Diagnostic,
167161-- and a value is in the Maybe. For operations that throw an error you
@@ -171,6 +165,9 @@ instance Hashable Key where
171165-- get empty diagnostics and a Nothing, to indicate this phase throws no fresh
172166-- errors but still failed.
173167--
168+-- A rule on a file should only return diagnostics for that given file. It should
169+-- not propagate diagnostic errors through multiple phases.
170+type IdeResult v = ([FileDiagnostic], Maybe v)
174171
175172data Value v
176173 = Succeeded TextDocumentVersion v
@@ -205,14 +202,14 @@ valueVersion = \case
205202 Failed -> Nothing
206203
207204mappingForVersion
208− :: HMap.HashMap NormalizedUri (Map TextDocumentVersion (a, PositionMapping))
205+ :: Map NormalizedUri (Map TextDocumentVersion PositionMapping)
209206 -> NormalizedFilePath
210207 -> TextDocumentVersion
211208 -> PositionMapping
212209mappingForVersion allMappings file ver =
213− maybe zeroMapping snd $
210+ fromMaybe idMapping $
214211 Map.lookup ver =<<
215− HMap.lookup (filePathToUri' file) allMappings
212+ Map.lookup (filePathToUri' file) allMappings
216213
217214type IdeRule k v =
218215 ( Shake.RuleResult k ~ v
@@ -243,6 +240,7 @@ shakeRunDatabaseProfile mbProfileDir shakeDb acts = do
243240 shakeProfileDatabase shakeDb $ dir </> file
244241 return (dir </> file)
245242 return (res, proFile)
243+ where
246244
247245{-# NOINLINE profileStartTime #-}
248246profileStartTime :: String
@@ -298,40 +296,34 @@ seqValue v b = case v of
298296shakeOpen :: IO LSP.LspId
299297 -> (LSP.FromServerMessage -> IO ()) -- ^ diagnostic handler
300298 -> Logger
301− -> Debouncer NormalizedUri
302299 -> Maybe FilePath
303300 -> IdeReportProgress
304301 -> ShakeOptions
305302 -> Rules ()
306303 -> IO IdeState
307−shakeOpen getLspId eventer logger debouncer shakeProfileDir (IdeReportProgress reportProgress) opts rules = do
308− inProgress <- newVar HMap.empty
304+shakeOpen getLspId eventer logger shakeProfileDir (IdeReportProgress reportProgress) opts rules = do
309305 shakeExtras <- do
310306 globals <- newVar HMap.empty
311307 state <- newVar HMap.empty
312308 diagnostics <- newVar mempty
313309 hiddenDiagnostics <- newVar mempty
314310 publishedDiagnostics <- newVar mempty
315− positionMapping <- newVar HMap.empty
311+ debouncer <- newDebouncer
312+ positionMapping <- newVar Map.empty
316313 pure ShakeExtras{..}
317314 (shakeDb, shakeClose) <-
318315 shakeOpenDatabase
319316 opts
320317 { shakeExtra = addShakeExtra shakeExtras $ shakeExtra opts
321− -- we don't actually use the progress value, but Shake conveniently spawns/kills this thread whenever
322− -- we call into Shake, so abuse it for that purpose
323− , shakeProgress = const $ if reportProgress then lspShakeProgress getLspId eventer inProgress else pure ()
318+ , shakeProgress = if reportProgress then lspShakeProgress getLspId eventer else const (pure ())
324319 }
325320 rules
326321 shakeAbort <- newMVar $ return ()
327322 shakeDb <- shakeDb
328323 return IdeState{..}
329324
330−lspShakeProgress :: Hashable a => IO LSP.LspId -> (LSP.FromServerMessage -> IO ()) -> Var (HMap.HashMap a Int) -> IO ()
331−lspShakeProgress getLspId sendMsg inProgress = do
332− -- first sleep a bit, so we only show progress messages if it's going to take
333− -- a "noticable amount of time" (we often expect a thread kill to arrive before the sleep finishes)
334− sleep 0.1
325+lspShakeProgress :: IO LSP.LspId -> (LSP.FromServerMessage -> IO ()) -> IO Progress -> IO ()
326+lspShakeProgress getLspId sendMsg prog = do
335327 lspId <- getLspId
336328 u <- ProgressTextToken . T.pack . show . hashUnique <$> newUnique
337329 sendMsg $ LSP.ReqWorkDoneProgressCreate $ LSP.fmServerWorkDoneProgressCreateRequest
@@ -359,9 +351,9 @@ lspShakeProgress getLspId sendMsg inProgress = do
359351 sample = 0.1
360352 loop id prev = do
361353 sleep sample
362− current <- readVar inProgress
363− let done = length $ filter (== 0) $ HMap.elems current
364− let todo = HMap.size current
354+ p <- prog
355+ let done = countSkipped p + countBuilt p
356+ let todo = done + countUnknown p + countTodo p
365357 let next = Just $ T.pack $ show done <> "/" <> show todo
366358 when (next /= prev) $
367359 sendMsg $ LSP.NotWorkDoneProgressReport $ LSP.fmServerWorkDoneProgressReportNotification
@@ -417,18 +409,16 @@ shakeRun IdeState{shakeExtras=ShakeExtras{..}, ..} acts =
417409 Right _ -> "completed"
418410 profile = case res of
419411 Right (_, Just fp) ->
420− let link = case filePathToUri' $ toNormalizedFilePath' fp of
421− NormalizedUri _ x -> x
412+ let link = case filePathToUri' $ toNormalizedFilePath fp of
413+ NormalizedUri x -> x
422414 in ", profile saved at " <> T.unpack link
423415 _ -> ""
424416 let logMsg = logDebug logger $ T.pack $
425417 "Finishing shakeRun (took " ++ showDuration runTime ++ ", " ++ res' ++ profile ++ ")"
426418 return (fst <$> res, logMsg)
427− let wrapUp (res, _) = do
419+ let wrapUp (res, logMsg) = do
420+ () <- logMsg
428421 either (throwIO @SomeException) return res
429− _ <- async $ do
430− (_, logMsg) <- wait aThread
431− logMsg
432422 pure (cancel aThread, wrapUp =<< wait aThread))
433423
434424getDiagnostics :: IdeState -> IO [FileDiagnostic]
@@ -456,9 +446,9 @@ garbageCollect keep = do
456446 return $! dupe values
457447 modifyVar_ diagnostics $ \diags -> return $! filterDiagnostics keep diags
458448 modifyVar_ hiddenDiagnostics $ \hdiags -> return $! filterDiagnostics keep hdiags
459− modifyVar_ publishedDiagnostics $ \diags -> return $! HMap.filterWithKey (\uri _ -> keep (fromUri uri)) diags
449+ modifyVar_ publishedDiagnostics $ \diags -> return $! Map.filterWithKey (\uri _ -> keep (fromUri uri)) diags
460450 let versionsForFile =
461− HMap.fromListWith Set.union $
451+ Map.fromListWith Set.union $
462452 mapMaybe (\((file, _key), v) -> (filePathToUri' file,) . Set.singleton <$> valueVersion v) $
463453 HMap.toList newState
464454 modifyVar_ positionMapping $ \mappings -> return $! filterVersionMap versionsForFile mappings
@@ -476,25 +466,25 @@ useWithStale :: IdeRule k v
476466useWithStale key file = head <$> usesWithStale key [file]
477467
478468useNoFile :: IdeRule k v => k -> Action (Maybe v)
479−useNoFile key = use key emptyFilePath
469+useNoFile key = use key ""
480470
481471use_ :: IdeRule k v => k -> NormalizedFilePath -> Action v
482472use_ key file = head <$> uses_ key [file]
483473
484474useNoFile_ :: IdeRule k v => k -> Action v
485−useNoFile_ key = use_ key emptyFilePath
475+useNoFile_ key = use_ key ""
486476
487477uses_ :: IdeRule k v => k -> [NormalizedFilePath] -> Action [v]
488478uses_ key files = do
489479 res <- uses key files
490480 case sequence res of
491− Nothing -> liftIO $ throwIO $ BadDependency (show key)
481+ Nothing -> liftIO $ throwIO BadDependency
492482 Just v -> return v
493483
494484
495485-- | When we depend on something that reported an error, and we fail as a direct result, throw BadDependency
496486-- which short-circuits the rest of the action
497−data BadDependency = BadDependency String deriving Show
487+data BadDependency = BadDependency deriving Show
498488instance Exception BadDependency
499489
500490isBadDependency :: SomeException -> Bool
@@ -513,10 +503,12 @@ instance Show k => Show (Q k) where
513503
514504-- | Invariant: the 'v' must be in normal form (fully evaluated).
515505-- Otherwise we keep repeatedly 'rnf'ing values taken from the Shake database
516−newtype A v = A (Value v)
506+-- Note (MK) I am not sure why we need the ShakeValue here, maybe we
507+-- can just remove it?
508+data A v = A (Value v) ShakeValue
517509 deriving Show
518510
519−instance NFData (A v) where rnf (A v) = v `seq` ()
511+instance NFData (A v) where rnf (A v x) = v `seq` rnf x
520512
521513-- In the Shake database we only store one type of key/result pairs,
522514-- namely Q (question) / A (answer).
@@ -526,70 +518,59 @@ type instance RuleResult (Q k) = A (RuleResult k)
526518-- | Return up2date results. Stale results will be ignored.
527519uses :: IdeRule k v
528520 => k -> [NormalizedFilePath] -> Action [Maybe v]
529−uses key files = map (\(A value) -> currentValue value) <$> apply (map (Q . (key,)) files)
521+uses key files = map (\(A value _) -> currentValue value) <$> apply (map (Q . (key,)) files)
530522
531523-- | Return the last computed result which might be stale.
532524usesWithStale :: IdeRule k v
533525 => k -> [NormalizedFilePath] -> Action [Maybe (v, PositionMapping)]
534526usesWithStale key files = do
535− values <- map (\(A value) -> value) <$> apply (map (Q . (key,)) files)
536− zipWithM lastValue files values
537−
538−
539−withProgress :: (Eq a, Hashable a) => Var (HMap.HashMap a Int) -> a -> Action b -> Action b
540−withProgress var file = actionBracket (f succ) (const $ f pred) . const
541− -- This functions are deliberately eta-expanded to avoid space leaks.
542− -- Do not remove the eta-expansion without profiling a session with at
543− -- least 1000 modifications.
544− where f shift = modifyVar_ var $ \x -> evaluate $ HMap.alter (\x -> Just $! shift (fromMaybe 0 x)) file x
545−
527+ values <- map (\(A value _) -> value) <$> apply (map (Q . (key,)) files)
528+ mapM (uncurry lastValue) (zip files values)
546529
547530defineEarlyCutoff
548531 :: IdeRule k v
549532 => (k -> NormalizedFilePath -> Action (Maybe BS.ByteString, IdeResult v))
550533 -> Rules ()
551534defineEarlyCutoff op = addBuiltinRule noLint noIdentity $ \(Q (key, file)) (old :: Maybe BS.ByteString) mode -> do
552− extras@ShakeExtras{state, inProgress} <- getShakeExtras
553− -- don't do progress for GetFileExists, as there are lots of non-nodes for just that one key
554− (if show key == "GetFileExists" then id else withProgress inProgress file) $ do
555− val <- case old of
556− Just old | mode == RunDependenciesSame -> do
557− v <- liftIO $ getValues state key file
558− case v of
559− -- No changes in the dependencies and we have
560− -- an existing result.
561− Just v -> return $ Just $ RunResult ChangedNothing old $ A v
562− _ -> return Nothing
563− _ -> return Nothing
564− case val of
565− Just res -> return res
566− Nothing -> do
567− (bs, (diags, res)) <- actionCatch
568− (do v <- op key file; liftIO $ evaluate $ force v) $
569− \(e :: SomeException) -> pure (Nothing, ([ideErrorText file $ T.pack $ show e | not $ isBadDependency e],Nothing))
570− modTime <- liftIO $ (currentValue =<<) <$> getValues state GetModificationTime file
571− (bs, res) <- case res of
572− Nothing -> do
573− staleV <- liftIO $ getValues state key file
574− pure $ case staleV of
575− Nothing -> (toShakeValue ShakeResult bs, Failed)
576− Just v -> case v of
577− Succeeded ver v -> (toShakeValue ShakeStale bs, Stale ver v)
578− Stale ver v -> (toShakeValue ShakeStale bs, Stale ver v)
579− Failed -> (toShakeValue ShakeResult bs, Failed)
580− Just v -> pure (maybe ShakeNoCutoff ShakeResult bs, Succeeded (vfsVersion =<< modTime) v)
581− liftIO $ setValues state key file res
582− updateFileDiagnostics file (Key key) extras $ map (\(_,y,z) -> (y,z)) diags
583− let eq = case (bs, fmap decodeShakeValue old) of
584− (ShakeResult a, Just (ShakeResult b)) -> a == b
585− (ShakeStale a, Just (ShakeStale b)) -> a == b
586− -- If we do not have a previous result
587− -- or we got ShakeNoCutoff we always return False.
588− _ -> False
589− return $ RunResult
590− (if eq then ChangedRecomputeSame else ChangedRecomputeDiff)
591− (encodeShakeValue bs) $
592− A res
535+ extras@ShakeExtras{state} <- getShakeExtras
536+ val <- case old of
537+ Just old | mode == RunDependenciesSame -> do
538+ v <- liftIO $ getValues state key file
539+ case v of
540+ -- No changes in the dependencies and we have
541+ -- an existing result.
542+ Just v -> return $ Just $ RunResult ChangedNothing old $ A v (decodeShakeValue old)
543+ _ -> return Nothing
544+ _ -> return Nothing
545+ case val of
546+ Just res -> return res
547+ Nothing -> do
548+ (bs, (diags, res)) <- actionCatch
549+ (do v <- op key file; liftIO $ evaluate $ force $ v) $
550+ \(e :: SomeException) -> pure (Nothing, ([ideErrorText file $ T.pack $ show e | not $ isBadDependency e],Nothing))
551+ modTime <- liftIO $ join . fmap currentValue <$> getValues state GetModificationTime file
552+ (bs, res) <- case res of
553+ Nothing -> do
554+ staleV <- liftIO $ getValues state key file
555+ pure $ case staleV of
556+ Nothing -> (toShakeValue ShakeResult bs, Failed)
557+ Just v -> case v of
558+ Succeeded ver v -> (toShakeValue ShakeStale bs, Stale ver v)
559+ Stale ver v -> (toShakeValue ShakeStale bs, Stale ver v)
560+ Failed -> (toShakeValue ShakeResult bs, Failed)
561+ Just v -> pure $ (maybe ShakeNoCutoff ShakeResult bs, Succeeded (vfsVersion =<< modTime) v)
562+ liftIO $ setValues state key file res
563+ updateFileDiagnostics file (Key key) extras $ map (\(_,y,z) -> (y,z)) diags
564+ let eq = case (bs, fmap decodeShakeValue old) of
565+ (ShakeResult a, Just (ShakeResult b)) -> a == b
566+ (ShakeStale a, Just (ShakeStale b)) -> a == b
567+ -- If we do not have a previous result
568+ -- or we got ShakeNoCutoff we always return False.
569+ _ -> False
570+ return $ RunResult
571+ (if eq then ChangedRecomputeSame else ChangedRecomputeDiff)
572+ (encodeShakeValue bs) $
573+ A res bs
593574
594575
595576-- | Rule type, input file
@@ -657,15 +638,21 @@ defineOnDisk act = addBuiltinRule noLint noIdentity $
657638 | otherwise = ChangedRecomputeDiff
658639 pure $ RunResult change (fromMaybe "" mbHash) (isJust mbHash)
659640
641+fingerprintToBS :: Fingerprint -> BS.ByteString
642+fingerprintToBS (Fingerprint a b) = BS.unsafeCreate 8 $ \ptr -> do
643+ ptr <- pure $ castPtr ptr
644+ pokeElemOff ptr 0 a
645+ pokeElemOff ptr 1 b
646+
660647needOnDisk :: (Shake.ShakeValue k, RuleResult k ~ ()) => k -> NormalizedFilePath -> Action ()
661648needOnDisk k file = do
662649 successfull <- apply1 (QDisk k file)
663− liftIO $ unless successfull $ throwIO $ BadDependency (show k)
650+ liftIO $ unless successfull $ throwIO BadDependency
664651
665652needOnDisks :: (Shake.ShakeValue k, RuleResult k ~ ()) => k -> [NormalizedFilePath] -> Action ()
666653needOnDisks k files = do
667654 successfulls <- apply $ map (QDisk k) files
668− liftIO $ unless (and successfulls) $ throwIO $ BadDependency (show k)
655+ liftIO $ unless (and successfulls) $ throwIO BadDependency
669656
670657toShakeValue :: (BS.ByteString -> ShakeValue) -> Maybe BS.ByteString -> ShakeValue
671658toShakeValue = maybe ShakeNoCutoff
@@ -704,7 +691,7 @@ updateFileDiagnostics ::
704691 -> [(ShowDiagnostic,Diagnostic)] -- ^ current results
705692 -> Action ()
706693updateFileDiagnostics fp k ShakeExtras{diagnostics, hiddenDiagnostics, publishedDiagnostics, state, debouncer, eventer} current = liftIO $ do
707− modTime <- (currentValue =<<) <$> getValues state GetModificationTime fp
694+ modTime <- join . fmap currentValue <$> getValues state GetModificationTime fp
708695 let (currentShown, currentHidden) = partition ((== ShowDiag) . fst) current
709696 mask_ $ do
710697 -- Mask async exceptions to ensure that updated diagnostics are always
@@ -717,7 +704,7 @@ updateFileDiagnostics fp k ShakeExtras{diagnostics, hiddenDiagnostics, published
717704 let newDiags = getFileDiagnostics fp newDiagsStore
718705 _ <- evaluate newDiagsStore
719706 _ <- evaluate newDiags
720− pure (newDiagsStore, newDiags)
707+ pure $! (newDiagsStore, newDiags)
721708 modifyVar_ hiddenDiagnostics $ \old -> do
722709 let newDiagsStore = setStageDiagnostics fp (vfsVersion =<< modTime)
723710 (T.pack $ show k) (map snd currentHidden) old
@@ -729,10 +716,10 @@ updateFileDiagnostics fp k ShakeExtras{diagnostics, hiddenDiagnostics, published
729716 let delay = if null newDiags then 0.1 else 0
730717 registerEvent debouncer delay uri $ do
731718 mask_ $ modifyVar_ publishedDiagnostics $ \published -> do
732− let lastPublish = HMap.lookupDefault [] uri published
719+ let lastPublish = Map.findWithDefault [] uri published
733720 when (lastPublish /= newDiags) $
734721 eventer $ publishDiagnosticsNotification (fromNormalizedUri uri) newDiags
735− pure $! HMap.insert uri newDiags published
722+ pure $! Map.insert uri newDiags published
736723
737724publishDiagnosticsNotification :: Uri -> [Diagnostic] -> LSP.FromServerMessage
738725publishDiagnosticsNotification uri diags =
@@ -743,7 +730,7 @@ publishDiagnosticsNotification uri diags =
743730newtype Priority = Priority Double
744731
745732setPriority :: Priority -> Action ()
746−setPriority (Priority p) = reschedule p
733+setPriority (Priority p) = deprioritize p
747734
748735sendEvent :: LSP.FromServerMessage -> Action ()
749736sendEvent e = do
@@ -768,22 +755,19 @@ instance Binary GetModificationTime
768755-- | Get the modification time of a file.
769756type instance RuleResult GetModificationTime = FileVersion
770757
771−data FileVersion
772− = VFSVersion !Int
773− | ModificationTime
774− !Int -- ^ Large unit (platform dependent, do not make assumptions)
775− !Int -- ^ Small unit (platform dependent, do not make assumptions)
758+-- | We store the modification time as a ByteString since we need
759+-- a ByteString anyway for Shake and we do not care about how times
760+-- are represented.
761+data FileVersion = VFSVersion Int | ModificationTime BS.ByteString
776762 deriving (Show, Generic)
777763
778764instance NFData FileVersion
779765
780766vfsVersion :: FileVersion -> Maybe Int
781767vfsVersion (VFSVersion i) = Just i
782−vfsVersion ModificationTime{} = Nothing
768+vfsVersion (ModificationTime _) = Nothing
769+
783770
784−modificationTime :: FileVersion -> Maybe (Int, Int)
785−modificationTime VFSVersion{} = Nothing
786−modificationTime (ModificationTime large small) = Just (large, small)
787771
788772getDiagnosticsFromStore :: StoreItem -> [Diagnostic]
789773getDiagnosticsFromStore (StoreItem _ diags) = concatMap SL.fromSortedList $ Map.elems diags
@@ -808,7 +792,7 @@ getAllDiagnostics ::
808792 DiagnosticStore ->
809793 [FileDiagnostic]
810794getAllDiagnostics =
811− concatMap (\(k,v) -> map (fromUri k,ShowDiag,) $ getDiagnosticsFromStore v) . HMap.toList
795+ concatMap (\(k,v) -> map (fromUri k,ShowDiag,) $ getDiagnosticsFromStore v) . Map.toList
812796
813797getFileDiagnostics ::
814798 NormalizedFilePath ->
@@ -816,34 +800,29 @@ getFileDiagnostics ::
816800 [LSP.Diagnostic]
817801getFileDiagnostics fp ds =
818802 maybe [] getDiagnosticsFromStore $
819− HMap.lookup (filePathToUri' fp) ds
803+ Map.lookup (filePathToUri' fp) ds
820804
821805filterDiagnostics ::
822806 (NormalizedFilePath -> Bool) ->
823807 DiagnosticStore ->
824808 DiagnosticStore
825809filterDiagnostics keep =
826− HMap.filterWithKey (\uri _ -> maybe True (keep . toNormalizedFilePath') $ uriToFilePath' $ fromNormalizedUri uri)
810+ Map.filterWithKey (\uri _ -> maybe True (keep . toNormalizedFilePath) $ uriToFilePath' $ fromNormalizedUri uri)
827811
828812filterVersionMap
829− :: HMap.HashMap NormalizedUri (Set.Set TextDocumentVersion)
830− -> HMap.HashMap NormalizedUri (Map TextDocumentVersion a)
831− -> HMap.HashMap NormalizedUri (Map TextDocumentVersion a)
813+ :: Map NormalizedUri (Set.Set TextDocumentVersion)
814+ -> Map NormalizedUri (Map TextDocumentVersion a)
815+ -> Map NormalizedUri (Map TextDocumentVersion a)
832816filterVersionMap =
833− HMap.intersectionWith $ \versionsToKeep versionMap -> Map.restrictKeys versionMap versionsToKeep
817+ Map.merge Map.dropMissing Map.dropMissing $
818+ Map.zipWithMatched $ \_ versionsToKeep versionMap -> Map.restrictKeys versionMap versionsToKeep
834819
835820updatePositionMapping :: IdeState -> VersionedTextDocumentIdentifier -> List TextDocumentContentChangeEvent -> IO ()
836−updatePositionMapping IdeState{shakeExtras = ShakeExtras{positionMapping}} VersionedTextDocumentIdentifier{..} (List changes) = do
821+updatePositionMapping IdeState{shakeExtras = ShakeExtras{positionMapping}} VersionedTextDocumentIdentifier{..} changes = do
837822 modifyVar_ positionMapping $ \allMappings -> do
838823 let uri = toNormalizedUri _uri
839− let mappingForUri = HMap.lookupDefault Map.empty uri allMappings
840− let (_, updatedMapping) =
841− -- Very important to use mapAccum here so that the tails of
842− -- each mapping can be shared, otherwise quadratic space is
843− -- used which is evident in long running sessions.
844− Map.mapAccumRWithKey (\acc _k (delta, _) -> let new = addDelta delta acc in (new, (delta, acc)))
845− zeroMapping
846− (Map.insert _version (shared_change, zeroMapping) mappingForUri)
847− pure $! HMap.insert uri updatedMapping allMappings
848− where
849− shared_change = mkDelta changes
824+ let mappingForUri = Map.findWithDefault Map.empty uri allMappings
825+ let updatedMapping =
826+ Map.insert _version idMapping $
827+ Map.map (\oldMapping -> foldl' applyChange oldMapping changes) mappingForUri
828+ pure $! Map.insert uri updatedMapping allMappings
src/Development/IDE/GHC/CPP.hs+4−27
@@ -19,8 +19,7 @@
1919--
2020-----------------------------------------------------------------------------
2121
22−module Development.IDE.GHC.CPP(doCpp, addOptP)
23−where
22+module Development.IDE.GHC.CPP(doCpp) where
2423
2524import Development.IDE.GHC.Compat
2625import Packages
@@ -30,14 +29,10 @@ import DynFlags
3029import Panic
3130import FileCleanup
3231#if MIN_GHC_API_VERSION(8,8,2)
33−import LlvmCodeGen (llvmVersionList)
32+import LlvmCodeGen (LlvmVersion, llvmVersionList)
3433#elif MIN_GHC_API_VERSION(8,8,0)
3534import LlvmCodeGen (LlvmVersion (..))
3635#endif
37−#if MIN_GHC_API_VERSION (8,10,0)
38−import Fingerprint
39−import ToolSettings
40−#endif
4136
4237import System.Directory
4338import System.FilePath
@@ -64,11 +59,7 @@ doCpp dflags raw input_fn output_fn = do
6459 let verbFlags = getVerbFlags dflags
6560
6661 let cpp_prog args | raw = SysTools.runCpp dflags args
67−#if MIN_GHC_API_VERSION(8,10,0)
68− | otherwise = SysTools.runCc Nothing
69−#else
7062 | otherwise = SysTools.runCc
71−#endif
7263 dflags (SysTools.Option "-E" : args)
7364
7465 let target_defs =
@@ -125,6 +116,8 @@ doCpp dflags raw input_fn output_fn = do
125116 ++ map SysTools.Option sse_defs
126117 ++ map SysTools.Option avx_defs
127118 ++ mb_macro_include
119+ -- Define a special macro "__GHCIDE__"
120+ ++ [ SysTools.Option "-D__GHCIDE__"]
128121 -- Set the language mode to assembler-with-cpp when preprocessing. This
129122 -- alleviates some of the C99 macro rules relating to whitespace and the hash
130123 -- operator, which we tend to abuse. Clang in particular is not very happy
@@ -167,22 +160,6 @@ getBackendDefs dflags | hscTarget dflags == HscLlvm = do
167160getBackendDefs _ =
168161 return []
169162
170−addOptP :: String -> DynFlags -> DynFlags
171−#if MIN_GHC_API_VERSION (8,10,0)
172−addOptP f = alterToolSettings $ \s -> s
173− { toolSettings_opt_P = f : toolSettings_opt_P s
174− , toolSettings_opt_P_fingerprint = fingerprintStrings (f : toolSettings_opt_P s)
175− }
176− where
177− fingerprintStrings ss = fingerprintFingerprints $ map fingerprintString ss
178− alterToolSettings f dynFlags = dynFlags { toolSettings = f (toolSettings dynFlags) }
179−#else
180−addOptP opt = onSettings (onOptP (opt:))
181− where
182− onSettings f x = x{settings = f $ settings x}
183− onOptP f x = x{sOpt_P = f $ sOpt_P x}
184−#endif
185−
186163-- ---------------------------------------------------------------------------
187164-- Macros (cribbed from Cabal)
188165
src/Development/IDE/GHC/Compat.hs+33−188
@@ -7,38 +7,23 @@
77
88-- | Attempt at hiding the GHC version differences we can.
99module Development.IDE.GHC.Compat(
10− getHeaderImports,
1110 HieFileResult(..),
12− HieFile,
13− hieExportNames,
14− hie_module,
11+ HieFile(..),
1512 mkHieFile,
1613 writeHieFile,
1714 readHieFile,
18− supportsHieFiles,
19− setDefaultHieDir,
20− dontWriteHieFiles,
21−#if !MIN_GHC_API_VERSION(8,8,0)
22− ml_hie_file,
23−#endif
2415 hPutStringBuffer,
2516 includePathsGlobal,
2617 includePathsQuote,
2718 addIncludePathsQuote,
28− getModuleHash,
29− getPackageName,
19+ ghcEnumerateExtensions,
3020 pattern DerivD,
3121 pattern ForD,
3222 pattern InstD,
3323 pattern TyClD,
3424 pattern ValD,
3525 pattern ClassOpSig,
36− pattern IEThingAll,
3726 pattern IEThingWith,
38− GHC.ModLocation,
39− Module.addBootSuffix,
40− pattern ModLocation,
41− getConArgs,
4227
4328 module GHC
4429 ) where
@@ -46,62 +31,46 @@ module Development.IDE.GHC.Compat(
4631import StringBuffer
4732import DynFlags
4833import FieldLabel
49−import Fingerprint (Fingerprint)
50−import qualified Module
51−import Packages
34+import GHC.LanguageExtensions.Type
5235
53−import qualified GHC
54−import GHC hiding (
55− ClassOpSig, DerivD, ForD, IEThingAll, IEThingWith, InstD, TyClD, ValD, ModLocation
56−#if MIN_GHC_API_VERSION(8,6,0)
57− , getConArgs
36+#if MIN_GHC_API_VERSION(8,8,0)
37+import Data.List.Extra (enumerate)
5838#endif
59− )
60−import qualified HeaderInfo as Hdr
61−import Avail
62−import ErrUtils (ErrorMessages)
63−import FastString (FastString)
6439
65−#if MIN_GHC_API_VERSION(8,10,0)
66−import HscTypes (mi_mod_hash)
67−#endif
40+import qualified GHC
41+import GHC hiding (ClassOpSig, DerivD, ForD, IEThingWith, InstD, TyClD, ValD)
6842
6943#if MIN_GHC_API_VERSION(8,8,0)
70−import Control.Applicative ((<|>))
71−import Development.IDE.GHC.HieAst (mkHieFile)
44+import HieAst
7245import HieBin
7346import HieTypes
74−
75−supportsHieFiles :: Bool
76−supportsHieFiles = True
77−
78−hieExportNames :: HieFile -> [(SrcSpan, Name)]
79−hieExportNames = nameListFromAvails . hie_exports
80−
8147#else
82−
83−#if MIN_GHC_API_VERSION(8,6,0)
84−import BinIface
85−import Data.IORef
86−import IfaceEnv
87−#endif
88−
89−import Binary
90−import Control.Exception (catch)
91−import Data.ByteString (ByteString)
92−import GhcPlugins hiding (ModLocation)
48+import GhcPlugins
9349import NameCache
50+import Avail
9451import TcRnTypes
9552import System.IO
9653import Foreign.ForeignPtr
97−import MkIface
9854
9955
56+#if !MIN_GHC_API_VERSION(8,8,0)
10057hPutStringBuffer :: Handle -> StringBuffer -> IO ()
10158hPutStringBuffer hdl (StringBuffer buf len cur)
10259 = withForeignPtr (plusForeignPtr buf cur) $ \ptr ->
10360 hPutBuf hdl ptr len
61+#endif
62+
63+mkHieFile :: ModSummary -> TcGblEnv -> RenamedSource -> Hsc HieFile
64+mkHieFile _ _ _ = return (HieFile () [])
65+
66+writeHieFile :: FilePath -> HieFile -> IO ()
67+writeHieFile _ _ = return ()
10468
69+readHieFile :: NameCache -> FilePath -> IO (HieFileResult, ())
70+readHieFile _ _ = return (HieFileResult (HieFile () []), ())
71+
72+data HieFile = HieFile {hie_module :: (), hie_exports :: [AvailInfo]}
73+data HieFileResult = HieFileResult { hie_file_result :: HieFile }
10574#endif
10675
10776#if !MIN_GHC_API_VERSION(8,6,0)
@@ -119,6 +88,15 @@ addIncludePathsQuote path x = x{includePaths = f $ includePaths x}
11988addIncludePathsQuote path x = x{includePaths = path : includePaths x}
12089#endif
12190
91+ghcEnumerateExtensions :: [Extension]
92+#if MIN_GHC_API_VERSION(8,8,0)
93+ghcEnumerateExtensions = enumerate
94+#elif MIN_GHC_API_VERSION(8,6,0)
95+ghcEnumerateExtensions = [Cpp .. StarIsType]
96+#else
97+ghcEnumerateExtensions = [Cpp .. EmptyDataDeriving]
98+#endif
99+
122100pattern DerivD :: DerivDecl p -> HsDecl p
123101pattern DerivD x <-
124102#if MIN_GHC_API_VERSION(8,6,0)
@@ -173,137 +151,4 @@ pattern IEThingWith a b c d <-
173151 GHC.IEThingWith _ a b c d
174152#else
175153 GHC.IEThingWith a b c d
176−#endif
177−
178−pattern ModLocation :: Maybe FilePath -> FilePath -> FilePath -> GHC.ModLocation
179−pattern ModLocation a b c <-
180−#if MIN_GHC_API_VERSION(8,8,0)
181− GHC.ModLocation a b c _ where ModLocation a b c = GHC.ModLocation a b c ""
182−#else
183− GHC.ModLocation a b c where ModLocation a b c = GHC.ModLocation a b c
184−#endif
185−
186−pattern IEThingAll :: LIEWrappedName (IdP pass) -> IE pass
187−pattern IEThingAll a <-
188−#if MIN_GHC_API_VERSION(8,6,0)
189− GHC.IEThingAll _ a
190−#else
191− GHC.IEThingAll a
192−#endif
193−
194−setDefaultHieDir :: FilePath -> DynFlags -> DynFlags
195−setDefaultHieDir _f d =
196−#if MIN_GHC_API_VERSION(8,8,0)
197− d { hieDir = hieDir d <|> Just _f}
198−#else
199− d
200−#endif
201−
202−dontWriteHieFiles :: DynFlags -> DynFlags
203−dontWriteHieFiles d =
204−#if MIN_GHC_API_VERSION(8,8,0)
205− gopt_unset d Opt_WriteHie
206−#else
207− d
208−#endif
209−
210−nameListFromAvails :: [AvailInfo] -> [(SrcSpan, Name)]
211−nameListFromAvails as =
212− map (\n -> (nameSrcSpan n, n)) (concatMap availNames as)
213−
214−#if !MIN_GHC_API_VERSION(8,8,0)
215−-- Reimplementations of functions for HIE files for GHC 8.6
216−
217−mkHieFile :: ModSummary -> TcGblEnv -> RenamedSource -> ByteString -> Hsc HieFile
218−mkHieFile ms ts _ _ = return (HieFile (ms_mod ms) es)
219− where
220− es = nameListFromAvails (mkIfaceExports (tcg_exports ts))
221−
222−ml_hie_file :: GHC.ModLocation -> FilePath
223−ml_hie_file ml = ml_hi_file ml ++ ".hie"
224−
225−data HieFile = HieFile {hie_module :: Module, hie_exports :: [(SrcSpan, Name)]}
226−
227−hieExportNames :: HieFile -> [(SrcSpan, Name)]
228−hieExportNames = hie_exports
229−
230−instance Binary HieFile where
231− put_ bh (HieFile m es) = do
232− put_ bh m
233− put_ bh es
234−
235− get bh = do
236− mod <- get bh
237− es <- get bh
238− return (HieFile mod es)
239−
240−data HieFileResult = HieFileResult { hie_file_result :: HieFile }
241−
242−writeHieFile :: FilePath -> HieFile -> IO ()
243−readHieFile :: NameCache -> FilePath -> IO (HieFileResult, ())
244−supportsHieFiles :: Bool
245−
246−#if MIN_GHC_API_VERSION(8,6,0)
247−
248−writeHieFile fp hie = do
249− bh <- openBinMem (1024 * 1024)
250− putWithUserData (const $ return ()) bh hie
251− writeBinMem bh fp
252−
253−readHieFile nc fp = do
254− bh <- readBinMem fp
255− nc' <- newIORef nc
256− hie_file <- getWithUserData (NCU (atomicModifyIORef' nc')) bh
257− return (HieFileResult hie_file, ())
258−
259−supportsHieFiles = True
260−
261−#else
262−
263−supportsHieFiles = False
264−
265−writeHieFile _ _ = return ()
266−
267−readHieFile _ _ = return undefined
268−
269−#endif
270−
271−#endif
272−
273−getHeaderImports
274− :: DynFlags
275− -> StringBuffer
276− -> FilePath
277− -> FilePath
278− -> IO
279− ( Either
280− ErrorMessages
281− ( [(Maybe FastString, Located ModuleName)]
282− , [(Maybe FastString, Located ModuleName)]
283− , Located ModuleName
284− )
285− )
286−#if MIN_GHC_API_VERSION(8,8,0)
287−getHeaderImports = Hdr.getImports
288−#else
289−getHeaderImports a b c d =
290− catch (Right <$> Hdr.getImports a b c d)
291− (return . Left . srcErrorMessages)
292−#endif
293−
294−getModuleHash :: ModIface -> Fingerprint
295−#if MIN_GHC_API_VERSION(8,10,0)
296−getModuleHash = mi_mod_hash . mi_final_exts
297−#else
298−getModuleHash = mi_mod_hash
299−#endif
300−
301−getConArgs :: ConDecl pass -> HsConDeclDetails pass
302−#if MIN_GHC_API_VERSION(8,6,0)
303−getConArgs = GHC.getConArgs
304−#else
305−getConArgs = GHC.getConDetails
306−#endif
307−
308−getPackageName :: DynFlags -> Module.InstalledUnitId -> Maybe PackageName
309−getPackageName dfs i = packageName <$> lookupPackage dfs (Module.DefiniteUnitId (Module.DefUnitId i))
154+#endif
src/Development/IDE/GHC/Error.hs+4−28
@@ -9,7 +9,6 @@ module Development.IDE.GHC.Error
99 , diagFromStrings
1010 , diagFromGhcException
1111 , catchSrcErrors
12− , mergeDiagnostics
1312
1413 -- * utilities working with spans
1514 , srcSpanToLocation
@@ -18,7 +17,6 @@ module Development.IDE.GHC.Error
1817 , zeroSpan
1918 , realSpan
2019 , isInsideSrcSpan
21− , noSpan
2220
2321 -- * utilities working with severities
2422 , toDSeverity
@@ -37,12 +35,11 @@ import Panic
3735import ErrUtils
3836import SrcLoc
3937import qualified Outputable as Out
40−import Exception (ExceptionMonad)
4138
4239
4340
4441diagFromText :: T.Text -> D.DiagnosticSeverity -> SrcSpan -> T.Text -> FileDiagnostic
45−diagFromText diagSource sev loc msg = (toNormalizedFilePath' $ srcSpanToFilename loc,ShowDiag,)
42+diagFromText diagSource sev loc msg = (toNormalizedFilePath $ srcSpanToFilename loc,ShowDiag,)
4643 Diagnostic
4744 { _range = srcSpanToRange loc
4845 , _severity = Just sev
@@ -50,39 +47,18 @@ diagFromText diagSource sev loc msg = (toNormalizedFilePath' $ srcSpanToFilename
5047 , _message = msg
5148 , _code = Nothing
5249 , _relatedInformation = Nothing
53− , _tags = Nothing
5450 }
5551
5652-- | Produce a GHC-style error from a source span and a message.
5753diagFromErrMsg :: T.Text -> DynFlags -> ErrMsg -> [FileDiagnostic]
5854diagFromErrMsg diagSource dflags e =
59− [ diagFromText diagSource sev (errMsgSpan e) $ T.pack $ Out.showSDoc dflags $
60− ErrUtils.formatErrDoc dflags $ ErrUtils.errMsgDoc e
55+ [ diagFromText diagSource sev (errMsgSpan e) $ T.pack $ Out.showSDoc dflags $ ErrUtils.pprLocErrMsg e
6156 | Just sev <- [toDSeverity $ errMsgSeverity e]]
6257
6358
6459diagFromErrMsgs :: T.Text -> DynFlags -> Bag ErrMsg -> [FileDiagnostic]
6560diagFromErrMsgs diagSource dflags = concatMap (diagFromErrMsg diagSource dflags) . bagToList
6661
67−-- | Merges two sorted lists of diagnostics, removing duplicates.
68−-- Assumes all the diagnostics are for the same file.
69−mergeDiagnostics :: [FileDiagnostic] -> [FileDiagnostic] -> [FileDiagnostic]
70−mergeDiagnostics aa [] = aa
71−mergeDiagnostics [] bb = bb
72−mergeDiagnostics (a@(_,_,ad@Diagnostic{_range = ar}):aa) (b@(_,_,bd@Diagnostic{_range=br}):bb)
73− | ar < br
74− = a : mergeDiagnostics aa (b:bb)
75− | br < ar
76− = b : mergeDiagnostics (a:aa) bb
77− | _severity ad == _severity bd
78− && _source ad == _source bd
79− && _message ad == _message bd
80− && _code ad == _code bd
81− && _relatedInformation ad == _relatedInformation bd
82− && _tags ad == _tags bd
83− = a : mergeDiagnostics aa bb
84− | otherwise
85− = a : b : mergeDiagnostics aa bb
8662
8763-- | Convert a GHC SrcSpan to a DAML compiler Range
8864srcSpanToRange :: SrcSpan -> Range
@@ -103,7 +79,7 @@ srcSpanToFilename (RealSrcSpan real) = FS.unpackFS $ srcSpanFile real
10379srcSpanToLocation :: SrcSpan -> Location
10480srcSpanToLocation src =
10581 -- important that the URI's we produce have been properly normalized, otherwise they point at weird places in VS Code
106− Location (fromNormalizedUri $ filePathToUri' $ toNormalizedFilePath' $ srcSpanToFilename src) (srcSpanToRange src)
82+ Location (fromNormalizedUri $ filePathToUri' $ toNormalizedFilePath $ srcSpanToFilename src) (srcSpanToRange src)
10783
10884isInsideSrcSpan :: Position -> SrcSpan -> Bool
10985p `isInsideSrcSpan` r = sp <= p && p <= ep
@@ -150,7 +126,7 @@ realSpan = \case
150126
151127-- | Run something in a Ghc monad and catch the errors (SourceErrors and
152128-- compiler-internal exceptions like Panic or InstallationError).
153−catchSrcErrors :: (HasDynFlags m, ExceptionMonad m) => T.Text -> m a -> m (Either [FileDiagnostic] a)
129+catchSrcErrors :: GhcMonad m => T.Text -> m a -> m (Either [FileDiagnostic] a)
154130catchSrcErrors fromWhere ghcM = do
155131 dflags <- getDynFlags
156132 handleGhcException (ghcExceptionToDiagnostics dflags) $
src/Development/IDE/GHC/Orphans.hs+3−10
@@ -1,10 +1,8 @@
11-- Copyright (c) 2019 The DAML Authors. All rights reserved.
22-- SPDX-License-Identifier: Apache-2.0
33
4−{-# LANGUAGE CPP #-}
54{-# LANGUAGE FlexibleInstances #-}
65{-# OPTIONS_GHC -Wno-orphans #-}
7−#include "ghc-api-version.h"
86
97-- | Orphan instances for GHC.
108-- Note that the 'NFData' instances may not be law abiding.
@@ -33,7 +31,7 @@ instance NFData Linkable where rnf = rwhnf
3331instance Show InstalledUnitId where
3432 show = installedUnitIdString
3533
36−instance NFData InstalledUnitId where rnf = rwhnf . installedUnitIdFS
34+instance NFData InstalledUnitId where rnf = rwhnf
3735
3836instance NFData SB.StringBuffer where rnf = rwhnf
3937
@@ -42,8 +40,8 @@ instance Show Module where
4240
4341instance Show (GenLocated SrcSpan ModuleName) where show = prettyPrint
4442
45−instance (NFData l, NFData e) => NFData (GenLocated l e) where
46− rnf (L l e) = rnf l `seq` rnf e
43+instance NFData (GenLocated SrcSpan ModuleName) where
44+ rnf = rwhnf
4745
4846instance Show ModSummary where
4947 show = show . ms_mod
@@ -54,11 +52,6 @@ instance Show ParsedModule where
5452instance NFData ModSummary where
5553 rnf = rwhnf
5654
57−#if !MIN_GHC_API_VERSION(8,10,0)
58−instance NFData FastString where
59− rnf = rwhnf
60−#endif
61−
6255instance NFData ParsedModule where
6356 rnf = rwhnf
6457
src/Development/IDE/GHC/Util.hs+65−146
@@ -1,83 +1,65 @@
11-- Copyright (c) 2019 The DAML Authors. All rights reserved.
22-- SPDX-License-Identifier: Apache-2.0
33
4−-- | General utility functions, mostly focused around GHC operations.
4+{-# OPTIONS_GHC -Wno-missing-fields #-} -- to enable prettyPrint
5+{-# LANGUAGE CPP #-}
6+#include "ghc-api-version.h"
7+
8+-- | GHC utility functions. Importantly, code using our GHC should never:
9+--
10+-- * Call runGhc, use runGhcFast instead. It's faster and doesn't require config we don't have.
11+--
12+-- * Call setSessionDynFlags, use modifyDynFlags instead. It's faster and avoids loading packages.
513module Development.IDE.GHC.Util(
6− -- * HcsEnv and environment
7− HscEnvEq(GhcVersionMismatch, compileTime, runTime), hscEnv, newHscEnvEq,
14+ lookupPackageConfig,
815 modifyDynFlags,
9− evalGhcEnv,
10− runGhcEnv,
11− deps,
12− -- * GHC wrappers
16+ fakeDynFlags,
1317 prettyPrint,
14− printRdrName,
15− printName,
16− ParseResult(..), runParser,
17− lookupPackageConfig,
18+ runGhcEnv,
1819 textToStringBuffer,
19− stringBufferToByteString,
2020 moduleImportPath,
21− cgGutsToCoreModule,
22− fingerprintToBS,
23− fingerprintFromStringBuffer,
24− -- * General utilities
21+ HscEnvEq, hscEnv, newHscEnvEq,
2522 readFileUtf8,
2623 hDuplicateTo',
27− setDefaultHieDir,
28− dontWriteHieFiles
24+ cgGutsToCoreModule
2925 ) where
3026
27+import Config
3128import Control.Concurrent
3229import Data.List.Extra
33−import Data.ByteString.Internal (ByteString(..))
3430import Data.Maybe
3531import Data.Typeable
36−import qualified Data.ByteString.Internal as BS
32+#if MIN_GHC_API_VERSION(8,6,0)
3733import Fingerprint
34+#endif
35+import GHC
3836import GhcMonad
39−import Control.Exception
37+import GhcPlugins hiding (Unique)
4038import Data.IORef
41−import Data.Version (showVersion, Version)
39+import Control.Exception
4240import FileCleanup
43−import Foreign.Ptr
44−import Foreign.ForeignPtr
45−import Foreign.Storable
4641import GHC.IO.BufferedIO (BufferedIO)
4742import GHC.IO.Device as IODevice
4843import GHC.IO.Encoding
4944import GHC.IO.Exception
5045import GHC.IO.Handle.Types
5146import GHC.IO.Handle.Internals
47+import Platform
5248import Data.Unique
5349import Development.Shake.Classes
5450import qualified Data.Text as T
5551import qualified Data.Text.Encoding as T
5652import qualified Data.Text.Encoding.Error as T
5753import qualified Data.ByteString as BS
58−import Lexer
5954import StringBuffer
6055import System.FilePath
61−import HscTypes (cg_binds, md_types, cg_module, ModDetails, CgGuts, ic_dflags, hsc_IC, HscEnv(hsc_dflags))
62−import PackageConfig (PackageConfig)
63−import Outputable (showSDocUnsafe, ppr, showSDoc, Outputable)
64−import Packages (getPackageConfigMap, lookupPackage')
65−import SrcLoc (mkRealSrcLoc)
66−import FastString (mkFastString)
67−import DynFlags (emptyFilesToClean, unsafeGlobalDynFlags)
68−import Module (moduleNameSlashes, InstalledUnitId)
69−import OccName (parenSymOcc)
70−import RdrName (nameRdrName, rdrNameOcc)
7156
72−import Development.IDE.GHC.Compat as GHC
7357import Development.IDE.Types.Location
7458
7559
7660----------------------------------------------------------------------
7761-- GHC setup
7862
79−-- | Used to modify dyn flags in preference to calling 'setSessionDynFlags',
80−-- since that function also reloads packages (which is very slow).
8163modifyDynFlags :: GhcMonad m => (DynFlags -> DynFlags) -> m ()
8264modifyDynFlags f = do
8365 newFlags <- f <$> getSessionDynFlags
@@ -86,7 +68,6 @@ modifyDynFlags f = do
8668 modifySession $ \h ->
8769 h { hsc_dflags = newFlags, hsc_IC = (hsc_IC h) {ic_dflags = newFlags} }
8870
89−-- | Given a 'UnitId' try and find the associated 'PackageConfig' in the environment.
9071lookupPackageConfig :: UnitId -> HscEnv -> Maybe PackageConfig
9172lookupPackageConfig unitId env =
9273 lookupPackage' False pkgConfigMap unitId
@@ -97,134 +78,86 @@ lookupPackageConfig unitId env =
9778 getPackageConfigMap $ hsc_dflags env
9879
9980
100−-- | Convert from the @text@ package to the @GHC@ 'StringBuffer'.
101−-- Currently implemented somewhat inefficiently (if it ever comes up in a profile).
81+-- would be nice to do this more efficiently...
10282textToStringBuffer :: T.Text -> StringBuffer
10383textToStringBuffer = stringToStringBuffer . T.unpack
10484
105−runParser :: DynFlags -> String -> P a -> ParseResult a
106−runParser flags str parser = unP parser parseState
107− where
108− filename = "<interactive>"
109− location = mkRealSrcLoc (mkFastString filename) 1 1
110− buffer = stringToStringBuffer str
111− parseState = mkPState flags buffer location
112−
113−stringBufferToByteString :: StringBuffer -> ByteString
114−stringBufferToByteString StringBuffer{..} = PS buf cur len
11585
116−-- | Pretty print a GHC value using 'unsafeGlobalDynFlags '.
11786prettyPrint :: Outputable a => a -> String
118−prettyPrint = showSDoc unsafeGlobalDynFlags . ppr
119−
120−-- | Pretty print a 'RdrName' wrapping operators in parens
121−printRdrName :: RdrName -> String
122−printRdrName name = showSDocUnsafe $ parenSymOcc rn (ppr rn)
123− where
124− rn = rdrNameOcc name
87+prettyPrint = showSDoc fakeDynFlags . ppr
12588
126−-- | Pretty print a 'Name' wrapping operators in parens
127−printName :: Name -> String
128−printName = printRdrName . nameRdrName
129−
130−-- | Run a 'Ghc' monad value using an existing 'HscEnv'. Sets up and tears down all the required
131−-- pieces, but designed to be more efficient than a standard 'runGhc'.
132−evalGhcEnv :: HscEnv -> Ghc b -> IO b
133−evalGhcEnv env act = snd <$> runGhcEnv env act
134−
135−-- | Run a 'Ghc' monad value using an existing 'HscEnv'. Sets up and tears down all the required
136−-- pieces, but designed to be more efficient than a standard 'runGhc'.
137−runGhcEnv :: HscEnv -> Ghc a -> IO (HscEnv, a)
89+runGhcEnv :: HscEnv -> Ghc a -> IO a
13890runGhcEnv env act = do
13991 filesToClean <- newIORef emptyFilesToClean
14092 dirsToClean <- newIORef mempty
14193 let dflags = (hsc_dflags env){filesToClean=filesToClean, dirsToClean=dirsToClean, useUnicode=True}
14294 ref <- newIORef env{hsc_dflags=dflags}
143− res <- unGhc act (Session ref) `finally` do
95+ unGhc act (Session ref) `finally` do
14496 cleanTempFiles dflags
14597 cleanTempDirs dflags
146− (,res) <$> readIORef ref
14798
148−-- | Given a module location, and its parse tree, figure out what is the include directory implied by it.
149−-- For example, given the file @\/usr\/\Test\/Foo\/Bar.hs@ with the module name @Foo.Bar@ the directory
150−-- @\/usr\/Test@ should be on the include path to find sibling modules.
151−moduleImportPath :: NormalizedFilePath -> GHC.ModuleName -> Maybe FilePath
99+-- Fake DynFlags which are mostly undefined, but define enough to do a
100+-- little bit.
101+fakeDynFlags :: DynFlags
102+fakeDynFlags = defaultDynFlags settings mempty
103+ where
104+ settings = Settings
105+ { sTargetPlatform = platform
106+ , sPlatformConstants = platformConstants
107+ , sProgramName = "ghc"
108+ , sProjectVersion = cProjectVersion
109+#if MIN_GHC_API_VERSION(8,6,0)
110+ , sOpt_P_fingerprint = fingerprint0
111+#endif
112+ }
113+ platform = Platform
114+ { platformWordSize=8
115+ , platformOS=OSUnknown
116+ , platformUnregisterised=True
117+ }
118+ platformConstants = PlatformConstants
119+ { pc_DYNAMIC_BY_DEFAULT=False
120+ , pc_WORD_SIZE=8
121+ }
122+
123+moduleImportPath :: NormalizedFilePath -> GHC.ParsedModule -> Maybe FilePath
152124-- The call to takeDirectory is required since DAML does not require that
153125-- the file name matches the module name in the last component.
154126-- Once that has changed we can get rid of this.
155−moduleImportPath (takeDirectory . fromNormalizedFilePath -> pathDir) mn
127+moduleImportPath (takeDirectory . fromNormalizedFilePath -> pathDir) pm
156128 -- This happens for single-component modules since takeDirectory "A" == "."
157129 | modDir == "." = Just pathDir
158130 | otherwise = dropTrailingPathSeparator <$> stripSuffix modDir pathDir
159131 where
132+ ms = GHC.pm_mod_summary pm
133+ mod' = GHC.ms_mod ms
160134 -- A for module A.B
161135 modDir =
162136 takeDirectory $
163− fromNormalizedFilePath $ toNormalizedFilePath' $
164− moduleNameSlashes mn
137+ fromNormalizedFilePath $ toNormalizedFilePath $
138+ moduleNameSlashes $ GHC.moduleName mod'
165139
166−-- | An 'HscEnv' with equality. Two values are considered equal
167−-- if they are created with the same call to 'newHscEnvEq'.
168−data HscEnvEq
169− = HscEnvEq !Unique !HscEnv
170− [(InstalledUnitId, DynFlags)] -- In memory components for this HscEnv
171− -- This is only used at the moment for the import dirs in
172− -- the DynFlags
173− | GhcVersionMismatch { compileTime :: !Version
174− , runTime :: !Version
175− }
140+-- | An HscEnv with equality.
141+data HscEnvEq = HscEnvEq Unique HscEnv
176142
177−-- | Unwrap an 'HsEnvEq'.
178143hscEnv :: HscEnvEq -> HscEnv
179−hscEnv = either error id . hscEnv'
180−
181−hscEnv' :: HscEnvEq -> Either String HscEnv
182−hscEnv' (HscEnvEq _ x _) = Right x
183−hscEnv' GhcVersionMismatch{..} = Left $
184− unwords
185− ["ghcide compiled against GHC"
186− ,showVersion compileTime
187− ,"but currently using"
188− ,showVersion runTime
189− ,". This is unsupported, ghcide must be compiled with the same GHC version as the project."
190− ]
191−
192−deps :: HscEnvEq -> [(InstalledUnitId, DynFlags)]
193−deps (HscEnvEq _ _ u) = u
194−deps GhcVersionMismatch{} = []
144+hscEnv (HscEnvEq _ x) = x
195145
196−-- | Wrap an 'HscEnv' into an 'HscEnvEq'.
197−newHscEnvEq :: HscEnv -> [(InstalledUnitId, DynFlags)] -> IO HscEnvEq
198−newHscEnvEq e uids = do u <- newUnique; return $ HscEnvEq u e uids
146+newHscEnvEq :: HscEnv -> IO HscEnvEq
147+newHscEnvEq e = do u <- newUnique; return $ HscEnvEq u e
199148
200149instance Show HscEnvEq where
201− show (HscEnvEq a _ _) = "HscEnvEq " ++ show (hashUnique a)
202− show GhcVersionMismatch{..} = "GhcVersionMismatch " <> show (compileTime, runTime)
150+ show (HscEnvEq a _) = "HscEnvEq " ++ show (hashUnique a)
203151
204152instance Eq HscEnvEq where
205− HscEnvEq a _ _ == HscEnvEq b _ _ = a == b
206− GhcVersionMismatch a b == GhcVersionMismatch c d = a == c && b == d
207− _ == _ = False
153+ HscEnvEq a _ == HscEnvEq b _ = a == b
208154
209155instance NFData HscEnvEq where
210− rnf (HscEnvEq a b c) = rnf (hashUnique a) `seq` b `seq` c `seq` ()
211− rnf GhcVersionMismatch{} = rnf runTime
156+ rnf (HscEnvEq a b) = rnf (hashUnique a) `seq` b `seq` ()
212157
213−instance Hashable HscEnvEq where
214− hashWithSalt s (HscEnvEq a _b _c) = hashWithSalt s a
215− hashWithSalt salt GhcVersionMismatch{..} = hashWithSalt salt (compileTime, runTime)
216−
217−-- Fake instance needed to persuade Shake to accept this type as a key.
218−-- No harm done as ghcide never persists these keys currently
219−instance Binary HscEnvEq where
220− put _ = error "not really"
221− get = error "not really"
222−
223−-- | Read a UTF8 file, with lenient decoding, so it will never raise a decoding error.
224158readFileUtf8 :: FilePath -> IO T.Text
225159readFileUtf8 f = T.decodeUtf8With T.lenientDecode <$> BS.readFile f
226160
227−-- | Convert from a 'CgGuts' to a 'CoreModule'.
228161cgGutsToCoreModule :: SafeHaskellMode -> CgGuts -> ModDetails -> CoreModule
229162cgGutsToCoreModule safeMode guts modDetails = CoreModule
230163 (cg_module guts)
@@ -232,22 +165,8 @@ cgGutsToCoreModule safeMode guts modDetails = CoreModule
232165 (cg_binds guts)
233166 safeMode
234167
235−-- | Convert a 'Fingerprint' to a 'ByteString' by copying the byte across.
236−-- Will produce an 8 byte unreadable ByteString.
237−fingerprintToBS :: Fingerprint -> BS.ByteString
238−fingerprintToBS (Fingerprint a b) = BS.unsafeCreate 8 $ \ptr -> do
239− ptr <- pure $ castPtr ptr
240− pokeElemOff ptr 0 a
241− pokeElemOff ptr 1 b
242−
243−-- | Take the 'Fingerprint' of a 'StringBuffer'.
244−fingerprintFromStringBuffer :: StringBuffer -> IO Fingerprint
245−fingerprintFromStringBuffer (StringBuffer buf len cur) =
246− withForeignPtr buf $ \ptr -> fingerprintData (ptr `plusPtr` cur) len
247−
248−
249−-- | A slightly modified version of 'hDuplicateTo' from GHC.
250−-- Importantly, it avoids the bug listed in https://gitlab.haskell.org/ghc/ghc/merge_requests/2318.
168+-- This is a slightly modified version of hDuplicateTo in GHC.
169+-- See the inline comment for more details.
251170hDuplicateTo' :: Handle -> Handle -> IO ()
252171hDuplicateTo' h1@(FileHandle path m1) h2@(FileHandle _ m2) = do
253172 withHandle__' "hDuplicateTo" h2 m2 $ \h2_ -> do
src/Development/IDE/GHC/WithDynFlags.hsdeleted−30
@@ -1,30 +0,0 @@
1−module Development.IDE.GHC.WithDynFlags
2−( WithDynFlags
3−, evalWithDynFlags
4−) where
5−
6−import Control.Monad.Trans.Reader (ask, ReaderT(..))
7−import GHC (DynFlags)
8−import Control.Monad.IO.Class (MonadIO)
9−import Exception (ExceptionMonad(..))
10−import Control.Monad.Trans.Class (MonadTrans(..))
11−import GhcPlugins (HasDynFlags(..))
12−
13−-- | A monad transformer implementing the 'HasDynFlags' effect
14−newtype WithDynFlags m a = WithDynFlags {withDynFlags :: ReaderT DynFlags m a}
15− deriving (Applicative, Functor, Monad, MonadIO, MonadTrans)
16−
17−evalWithDynFlags :: DynFlags -> WithDynFlags m a -> m a
18−evalWithDynFlags dflags = flip runReaderT dflags . withDynFlags
19−
20−instance Monad m => HasDynFlags (WithDynFlags m) where
21− getDynFlags = WithDynFlags ask
22−
23−instance ExceptionMonad m => ExceptionMonad (WithDynFlags m) where
24− gmask f = WithDynFlags $ ReaderT $ \env ->
25− gmask $ \restore ->
26− let restore' = lift . restore . flip runReaderT env . withDynFlags
27− in runReaderT (withDynFlags $ f restore') env
28−
29− gcatch (WithDynFlags act) handle = WithDynFlags $ ReaderT $ \env ->
30− gcatch (runReaderT act env) (flip runReaderT env . withDynFlags . handle)
src/Development/IDE/Import/DependencyInformation.hs+34−121
@@ -9,33 +9,26 @@ module Development.IDE.Import.DependencyInformation
99 , ModuleParseError(..)
1010 , TransitiveDependencies(..)
1111 , FilePathId(..)
12− , NamedModuleDep(..)
1312
1413 , PathIdMap
1514 , emptyPathIdMap
1615 , getPathId
17− , lookupPathToId
1816 , insertImport
1917 , pathToId
2018 , idToPath
2119 , reachableModules
20+
2221 , processDependencyInformation
2322 , transitiveDeps
24−
25− , BootIdMap
26− , insertBootId
2723 ) where
2824
2925import Control.DeepSeq
3026import Data.Bifunctor
3127import Data.Coerce
3228import Data.List
33−import Data.Tuple.Extra hiding (first, second)
3429import Development.IDE.GHC.Orphans()
3530import Data.Either
3631import Data.Graph
37−import Data.HashMap.Strict (HashMap)
38−import qualified Data.HashMap.Strict as HMS
3932import Data.List.NonEmpty (NonEmpty(..), nonEmpty)
4033import qualified Data.List.NonEmpty as NonEmpty
4134import Data.IntMap (IntMap)
@@ -43,14 +36,16 @@ import qualified Data.IntMap.Strict as IntMap
4336import qualified Data.IntMap.Lazy as IntMapLazy
4437import Data.IntSet (IntSet)
4538import qualified Data.IntSet as IntSet
39+import Data.Map (Map)
40+import qualified Data.Map.Strict as MS
4641import Data.Maybe
4742import Data.Set (Set)
4843import qualified Data.Set as Set
44+import Data.Tuple.Extra (fst3)
4945import GHC.Generics (Generic)
5046
5147import Development.IDE.Types.Diagnostics
5248import Development.IDE.Types.Location
53−import Development.IDE.Import.FindImports (ArtifactsLocation(..))
5449
5550import GHC
5651import Module
@@ -62,100 +57,69 @@ data ModuleImports = ModuleImports
6257 -- that module on disk (if we found it)
6358 , packageImports :: !(Set InstalledUnitId)
6459 -- ^ Transitive package dependencies unioned for all imports.
65− } deriving Show
60+ }
6661
67−-- | For processing dependency information, we need lots of maps and sets of
68−-- filepaths. Comparing Strings is really slow, so we work with IntMap/IntSet
69−-- instead and only convert at the edges.
62+-- | For processing dependency information, we need lots of maps and sets
63+-- of filepaths. Comparing Strings is really slow, so we work with IntMap/IntSet
64+-- instead and only convert at the edges
65+-- and
7066newtype FilePathId = FilePathId { getFilePathId :: Int }
7167 deriving (Show, NFData, Eq, Ord)
7268
73−-- | Map from 'FilePathId'
74−type FilePathIdMap = IntMap
75−
76−-- | Set of 'FilePathId's
77−type FilePathIdSet = IntSet
78−
7969data PathIdMap = PathIdMap
80− { idToPathMap :: !(FilePathIdMap ArtifactsLocation)
81− , pathToIdMap :: !(HashMap NormalizedFilePath FilePathId)
70+ { idToPathMap :: !(IntMap NormalizedFilePath)
71+ , pathToIdMap :: !(Map NormalizedFilePath FilePathId)
8272 }
8373 deriving (Show, Generic)
8474
8575instance NFData PathIdMap
8676
8777emptyPathIdMap :: PathIdMap
88−emptyPathIdMap = PathIdMap IntMap.empty HMS.empty
78+emptyPathIdMap = PathIdMap IntMap.empty MS.empty
8979
90−getPathId :: ArtifactsLocation -> PathIdMap -> (FilePathId, PathIdMap)
80+getPathId :: NormalizedFilePath -> PathIdMap -> (FilePathId, PathIdMap)
9181getPathId path m@PathIdMap{..} =
92− case HMS.lookup (artifactFilePath path) pathToIdMap of
82+ case MS.lookup path pathToIdMap of
9383 Nothing ->
94− let !newId = FilePathId $ HMS.size pathToIdMap
84+ let !newId = FilePathId $ MS.size pathToIdMap
9585 in (newId, insertPathId path newId m)
9686 Just id -> (id, m)
9787
98−insertPathId :: ArtifactsLocation -> FilePathId -> PathIdMap -> PathIdMap
88+insertPathId :: NormalizedFilePath -> FilePathId -> PathIdMap -> PathIdMap
9989insertPathId path id PathIdMap{..} =
100− PathIdMap (IntMap.insert (getFilePathId id) path idToPathMap) (HMS.insert (artifactFilePath path) id pathToIdMap)
90+ PathIdMap (IntMap.insert (getFilePathId id) path idToPathMap) (MS.insert path id pathToIdMap)
10191
10292insertImport :: FilePathId -> Either ModuleParseError ModuleImports -> RawDependencyInformation -> RawDependencyInformation
10393insertImport (FilePathId k) v rawDepInfo = rawDepInfo { rawImports = IntMap.insert k v (rawImports rawDepInfo) }
10494
10595pathToId :: PathIdMap -> NormalizedFilePath -> FilePathId
106−pathToId PathIdMap{pathToIdMap} path = pathToIdMap HMS.! path
107−
108−lookupPathToId :: PathIdMap -> NormalizedFilePath -> Maybe FilePathId
109−lookupPathToId PathIdMap{pathToIdMap} path = HMS.lookup path pathToIdMap
96+pathToId PathIdMap{pathToIdMap} path = pathToIdMap MS.! path
11097
11198idToPath :: PathIdMap -> FilePathId -> NormalizedFilePath
112−idToPath pathIdMap filePathId = artifactFilePath $ idToModLocation pathIdMap filePathId
113−
114−idToModLocation :: PathIdMap -> FilePathId -> ArtifactsLocation
115−idToModLocation PathIdMap{idToPathMap} (FilePathId id) = idToPathMap IntMap.! id
116−
117−type BootIdMap = FilePathIdMap FilePathId
118−
119−insertBootId :: FilePathId -> FilePathId -> BootIdMap -> BootIdMap
120−insertBootId k = IntMap.insert (getFilePathId k)
99+idToPath PathIdMap{idToPathMap} (FilePathId id) = idToPathMap IntMap.! id
121100
122101-- | Unprocessed results that we find by following imports recursively.
123102data RawDependencyInformation = RawDependencyInformation
124− { rawImports :: !(FilePathIdMap (Either ModuleParseError ModuleImports))
103+ { rawImports :: !(IntMap (Either ModuleParseError ModuleImports))
125104 , rawPathIdMap :: !PathIdMap
126− -- The rawBootMap maps the FilePathId of a hs-boot file to its
127− -- corresponding hs file. It is used when topologically sorting as we
128− -- need to add edges between .hs-boot and .hs so that the .hs files
129− -- appear later in the sort.
130− , rawBootMap :: !BootIdMap
131− } deriving Show
132−
133−pkgDependencies :: RawDependencyInformation -> FilePathIdMap (Set InstalledUnitId)
105+ }
106+
107+pkgDependencies :: RawDependencyInformation -> IntMap (Set InstalledUnitId)
134108pkgDependencies RawDependencyInformation{..} =
135109 IntMap.map (either (const Set.empty) packageImports) rawImports
136110
137111data DependencyInformation =
138112 DependencyInformation
139− { depErrorNodes :: !(FilePathIdMap (NonEmpty NodeError))
113+ { depErrorNodes :: !(IntMap (NonEmpty NodeError))
140114 -- ^ Nodes that cannot be processed correctly.
141− , depModuleNames :: !(FilePathIdMap ShowableModuleName)
142− , depModuleDeps :: !(FilePathIdMap FilePathIdSet)
115+ , depModuleDeps :: !(IntMap IntSet)
143116 -- ^ For a non-error node, this contains the set of module immediate dependencies
144117 -- in the same package.
145− , depPkgDeps :: !(FilePathIdMap (Set InstalledUnitId))
118+ , depPkgDeps :: !(IntMap (Set InstalledUnitId))
146119 -- ^ For a non-error node, this contains the set of immediate pkg deps.
147120 , depPathIdMap :: !PathIdMap
148− -- ^ Map from FilePath to FilePathId
149− , depBootMap :: !BootIdMap
150− -- ^ Map from hs-boot file to the corresponding hs file
151121 } deriving (Show, Generic)
152122
153−newtype ShowableModuleName =
154− ShowableModuleName {showableModuleName :: ModuleName}
155− deriving NFData
156−
157−instance Show ShowableModuleName where show = moduleNameString . showableModuleName
158−
159123reachableModules :: DependencyInformation -> [NormalizedFilePath]
160124reachableModules DependencyInformation{..} =
161125 map (idToPath depPathIdMap . FilePathId) $ IntMap.keys depErrorNodes <> IntMap.keys depModuleDeps
@@ -222,32 +186,23 @@ processDependencyInformation rawDepInfo@RawDependencyInformation{..} =
222186 DependencyInformation
223187 { depErrorNodes = IntMap.fromList errorNodes
224188 , depModuleDeps = moduleDeps
225− , depModuleNames = IntMap.fromList $ coerce moduleNames
226189 , depPkgDeps = pkgDependencies rawDepInfo
227190 , depPathIdMap = rawPathIdMap
228− , depBootMap = rawBootMap
229191 }
230192 where resultGraph = buildResultGraph rawImports
231193 (errorNodes, successNodes) = partitionNodeResults $ IntMap.toList resultGraph
232− moduleNames :: [(FilePathId, ModuleName)]
233− moduleNames =
234− [ (fId, modName) | (_, imports) <- successNodes, (L _ modName, fId) <- imports]
235194 successEdges :: [(FilePathId, FilePathId, [FilePathId])]
236195 successEdges =
237− map
238− (\(file, imports) -> (FilePathId file, FilePathId file, map snd imports))
239− successNodes
196+ map (\(file, imports) -> (FilePathId file, FilePathId file, map snd imports)) successNodes
240197 moduleDeps =
241− IntMap.fromList $
242− map (\(_, FilePathId v, vs) -> (v, IntSet.fromList $ coerce vs))
243− successEdges
198+ IntMap.fromList $ map (\(_, FilePathId v, vs) -> (v, IntSet.fromList $ coerce vs)) successEdges
244199
245200-- | Given a dependency graph, buildResultGraph detects and propagates errors in that graph as follows:
246201-- 1. Mark each node that is part of an import cycle as an error node.
247202-- 2. Mark each node that has a parse error as an error node.
248203-- 3. Mark each node whose immediate children could not be located as an error.
249204-- 4. Recursively propagate errors to parents if they are not already error nodes.
250−buildResultGraph :: FilePathIdMap (Either ModuleParseError ModuleImports) -> FilePathIdMap NodeResult
205+buildResultGraph :: IntMap (Either ModuleParseError ModuleImports) -> IntMap NodeResult
251206buildResultGraph g = propagatedErrors
252207 where
253208 sccs = stronglyConnComp (graphEdges g)
@@ -294,7 +249,7 @@ buildResultGraph g = propagatedErrors
294249 Right ModuleImports{moduleImports} ->
295250 fmap fst $ find (\(_, resolvedImp) -> resolvedImp == Just importedFile) moduleImports
296251
297−graphEdges :: FilePathIdMap (Either ModuleParseError ModuleImports) -> [(FilePathId, FilePathId, [FilePathId])]
252+graphEdges :: IntMap (Either ModuleParseError ModuleImports) -> [(FilePathId, FilePathId, [FilePathId])]
298253graphEdges g =
299254 map (\(k, v) -> (FilePathId k, FilePathId k, deps v)) $ IntMap.toList g
300255 where deps :: Either e ModuleImports -> [FilePathId]
@@ -306,71 +261,29 @@ partitionSCC (CyclicSCC xs:rest) = second (xs:) $ partitionSCC rest
306261partitionSCC (AcyclicSCC x:rest) = first (x:) $ partitionSCC rest
307262partitionSCC [] = ([], [])
308263
309−
310264transitiveDeps :: DependencyInformation -> NormalizedFilePath -> Maybe TransitiveDependencies
311265transitiveDeps DependencyInformation{..} file = do
312266 let !fileId = pathToId depPathIdMap file
313267 reachableVs <-
314− -- Delete the starting node
315268 IntSet.delete (getFilePathId fileId) .
316269 IntSet.fromList . map (fst3 . fromVertex) .
317270 reachable g <$> toVertex (getFilePathId fileId)
318− let transitiveModuleDepIds =
319− filter (\v -> v `IntSet.member` reachableVs) $ map (fst3 . fromVertex) vs
271+ let transitiveModuleDepIds = filter (\v -> v `IntSet.member` reachableVs) $ map (fst3 . fromVertex) vs
320272 let transitivePkgDeps =
321273 Set.toList $ Set.unions $
322274 map (\f -> IntMap.findWithDefault Set.empty f depPkgDeps) $
323275 getFilePathId fileId : transitiveModuleDepIds
324− let transitiveModuleDeps =
325− map (idToPath depPathIdMap . FilePathId) transitiveModuleDepIds
326− let transitiveNamedModuleDeps =
327− [ NamedModuleDep (idToPath depPathIdMap (FilePathId fid)) mn artifactModLocation
328− | (fid, ShowableModuleName mn) <- IntMap.toList depModuleNames
329− , let ArtifactsLocation{artifactModLocation} = idToPathMap depPathIdMap IntMap.! fid
330− ]
276+ let transitiveModuleDeps = map (idToPath depPathIdMap . FilePathId) transitiveModuleDepIds
331277 pure TransitiveDependencies {..}
332− where
333− (g, fromVertex, toVertex) = graphFromEdges edges
334− edges = map (\(f, fs) -> (f, f, IntSet.toList fs ++ boot_edge f)) $ IntMap.toList depModuleDeps
335−
336− -- Need to add an edge between the .hs and .hs-boot file if it exists
337− -- so the .hs file gets loaded after the .hs-boot file and the right
338− -- stuff ends up in the HPT. If you don't have this check then GHC will
339− -- fail to work with ghcide.
340− boot_edge f = [getFilePathId f' | Just f' <- [IntMap.lookup f depBootMap]]
341−
342− vs = topSort g
278+ where (g, fromVertex, toVertex) = graphFromEdges (map (\(f, fs) -> (f, f, IntSet.toList fs)) $ IntMap.toList depModuleDeps)
279+ vs = topSort g
343280
344281data TransitiveDependencies = TransitiveDependencies
345282 { transitiveModuleDeps :: [NormalizedFilePath]
346283 -- ^ Transitive module dependencies in topological order.
347284 -- The module itself is not included.
348− , transitiveNamedModuleDeps :: [NamedModuleDep]
349− -- ^ Transitive module dependencies in topological order.
350− -- The module itself is not included.
351285 , transitivePkgDeps :: [InstalledUnitId]
352286 -- ^ Transitive pkg dependencies in unspecified order.
353287 } deriving (Eq, Show, Generic)
354288
355289instance NFData TransitiveDependencies
356−
357−data NamedModuleDep = NamedModuleDep {
358− nmdFilePath :: !NormalizedFilePath,
359− nmdModuleName :: !ModuleName,
360− nmdModLocation :: !ModLocation
361− }
362− deriving Generic
363−
364−instance Eq NamedModuleDep where
365− a == b = nmdFilePath a == nmdFilePath b
366−
367−instance NFData NamedModuleDep where
368− rnf NamedModuleDep{..} =
369− rnf nmdFilePath `seq`
370− rnf nmdModuleName `seq`
371− -- 'ModLocation' lacks an 'NFData' instance
372− rwhnf nmdModLocation
373−
374−instance Show NamedModuleDep where
375− show NamedModuleDep{..} = show nmdFilePath
376−
src/Development/IDE/Import/FindImports.hs+17−63
@@ -7,19 +7,16 @@
77module Development.IDE.Import.FindImports
88 ( locateModule
99 , Import(..)
10− , ArtifactsLocation(..)
11− , modSummaryToArtifactsLocation
12− , isBootLocation
13− , mkImportDirs
1410 ) where
1511
1612import Development.IDE.GHC.Error as ErrUtils
1713import Development.IDE.GHC.Orphans()
1814import Development.IDE.Types.Diagnostics
1915import Development.IDE.Types.Location
20−import Development.IDE.GHC.Compat
2116-- GHC imports
17+import DynFlags
2218import FastString
19+import GHC
2320import qualified Module as M
2421import Packages
2522import Outputable (showSDoc, ppr, pprPanic)
@@ -30,107 +27,64 @@ import Control.DeepSeq
3027import Control.Monad.Extra
3128import Control.Monad.IO.Class
3229import System.FilePath
33−import DriverPhases
34−import Data.Maybe
3530
3631data Import
37− = FileImport !ArtifactsLocation
32+ = FileImport !NormalizedFilePath
3833 | PackageImport !M.InstalledUnitId
3934 deriving (Show)
4035
41−data ArtifactsLocation = ArtifactsLocation
42− { artifactFilePath :: !NormalizedFilePath
43− , artifactModLocation :: !ModLocation
44− , artifactIsSource :: !Bool -- ^ True if a module is a source input
45− }
46− deriving (Show)
47−
48−instance NFData ArtifactsLocation where
49− rnf ArtifactsLocation{..} = rnf artifactFilePath `seq` rwhnf artifactModLocation `seq` rnf artifactIsSource
50−
51−isBootLocation :: ArtifactsLocation -> Bool
52−isBootLocation = not . artifactIsSource
53−
5436instance NFData Import where
5537 rnf (FileImport x) = rnf x
5638 rnf (PackageImport x) = rnf x
5739
58−modSummaryToArtifactsLocation :: NormalizedFilePath -> ModSummary -> ArtifactsLocation
59−modSummaryToArtifactsLocation nfp ms = ArtifactsLocation nfp (ms_location ms) (isSource (ms_hsc_src ms))
60− where
61− isSource HsSrcFile = True
62− isSource _ = False
63−
6440
6541-- | locate a module in the file system. Where we go from *daml to Haskell
6642locateModuleFile :: MonadIO m
67− => [[FilePath]]
43+ => DynFlags
6844 -> [String]
6945 -> (NormalizedFilePath -> m Bool)
7046 -> Bool
7147 -> ModuleName
7248 -> m (Maybe NormalizedFilePath)
73−locateModuleFile import_dirss exts doesExist isSource modName = do
74− let candidates import_dirs =
75− [ toNormalizedFilePath' (prefix </> M.moduleNameSlashes modName <.> maybeBoot ext)
76− | prefix <- import_dirs , ext <- exts]
77− findM doesExist (concatMap candidates import_dirss)
49+locateModuleFile dflags exts doesExist isSource modName = do
50+ let candidates =
51+ [ toNormalizedFilePath (prefix </> M.moduleNameSlashes modName <.> maybeBoot ext)
52+ | prefix <- importPaths dflags, ext <- exts]
53+ findM doesExist candidates
7854 where
7955 maybeBoot ext
8056 | isSource = ext ++ "-boot"
8157 | otherwise = ext
8258
83−-- | This function is used to map a package name to a set of import paths.
84−-- It only returns Just for unit-ids which are possible to import into the
85−-- current module. In particular, it will return Nothing for 'main' components
86−-- as they can never be imported into another package.
87−mkImportDirs :: DynFlags -> (M.InstalledUnitId, DynFlags) -> Maybe (PackageName, [FilePath])
88−mkImportDirs df (i, DynFlags{importPaths}) = (, importPaths) <$> getPackageName df i
89−
9059-- | locate a module in either the file system or the package database. Where we go from *daml to
9160-- Haskell
9261locateModule
9362 :: MonadIO m
9463 => DynFlags
95− -> [(M.InstalledUnitId, DynFlags)] -- Sets import directories to look in
9664 -> [String]
9765 -> (NormalizedFilePath -> m Bool)
9866 -> Located ModuleName
9967 -> Maybe FastString
10068 -> Bool
10169 -> m (Either [FileDiagnostic] Import)
102−locateModule dflags comp_info exts doesExist modName mbPkgName isSource = do
70+locateModule dflags exts doesExist modName mbPkgName isSource = do
10371 case mbPkgName of
10472 -- "this" means that we should only look in the current package
10573 Just "this" -> do
106− lookupLocal [importPaths dflags]
74+ mbFile <- locateModuleFile dflags exts doesExist isSource $ unLoc modName
75+ case mbFile of
76+ Nothing -> return $ Left $ notFoundErr dflags modName $ LookupNotFound []
77+ Just file -> return $ Right $ FileImport file
10778 -- if a package name is given we only go look for a package
108− Just pkgName
109− | Just dirs <- lookup (PackageName pkgName) import_paths
110− -> lookupLocal [dirs]
111− | otherwise -> lookupInPackageDB dflags
79+ Just _pkgName -> lookupInPackageDB dflags
11280 Nothing -> do
11381 -- first try to find the module as a file. If we can't find it try to find it in the package
11482 -- database.
115− -- Here the importPaths for the current modules are added to the front of the import paths from the other components.
116− -- This is particularly important for Paths_* modules which get generated for every component but unless you use it in
117− -- each component will end up being found in the wrong place and cause a multi-cradle match failure.
118− mbFile <- locateModuleFile (importPaths dflags : map snd import_paths) exts doesExist isSource $ unLoc modName
83+ mbFile <- locateModuleFile dflags exts doesExist isSource $ unLoc modName
11984 case mbFile of
12085 Nothing -> lookupInPackageDB dflags
121− Just file -> toModLocation file
86+ Just file -> return $ Right $ FileImport file
12287 where
123− import_paths = mapMaybe (mkImportDirs dflags) comp_info
124− toModLocation file = liftIO $ do
125− loc <- mkHomeModLocation dflags (unLoc modName) (fromNormalizedFilePath file)
126− return $ Right $ FileImport $ ArtifactsLocation file loc (not isSource)
127−
128− lookupLocal dirs = do
129− mbFile <- locateModuleFile dirs exts doesExist isSource $ unLoc modName
130− case mbFile of
131− Nothing -> return $ Left $ notFoundErr dflags modName $ LookupNotFound []
132− Just file -> toModLocation file
133−
13488 lookupInPackageDB dfs =
13589 case lookupModuleWithSuggestions dfs (unLoc modName) mbPkgName of
13690 LookupFound _m pkgConfig -> return $ Right $ PackageImport $ unitId pkgConfig
src/Development/IDE/Plugin/CodeAction.hs → src/Development/IDE/LSP/CodeAction.hsrenamed+112−177
@@ -6,35 +6,24 @@
66#include "ghc-api-version.h"
77
88-- | Go to the definition of a variable.
9−module Development.IDE.Plugin.CodeAction
10− (
11− plugin
12−
13− -- * For haskell-language-server
14− , codeAction
15− , codeLens
16− , rulePackageExports
17− , executeAddSignatureCommand
9+module Development.IDE.LSP.CodeAction
10+ ( setHandlersCodeAction
11+ , setHandlersCodeLens
1812 ) where
1913
2014import Language.Haskell.LSP.Types
2115import Control.Monad (join)
22−import Development.IDE.Plugin
2316import Development.IDE.GHC.Compat
2417import Development.IDE.Core.Rules
2518import Development.IDE.Core.RuleTypes
2619import Development.IDE.Core.Service
2720import Development.IDE.Core.Shake
2821import Development.IDE.GHC.Error
29−import Development.IDE.GHC.Util
3022import Development.IDE.LSP.Server
31−import Development.IDE.Plugin.CodeAction.PositionIndexed
32−import Development.IDE.Plugin.CodeAction.RuleTypes
33−import Development.IDE.Plugin.CodeAction.Rules
3423import Development.IDE.Types.Location
3524import Development.IDE.Types.Options
36−import Development.Shake (Rules)
3725import qualified Data.HashMap.Strict as Map
26+import qualified Data.HashSet as Set
3827import qualified Language.Haskell.LSP.Core as LSP
3928import Language.Haskell.LSP.VFS
4029import Language.Haskell.LSP.Messages
@@ -45,98 +34,69 @@ import Data.Char
4534import Data.Maybe
4635import Data.List.Extra
4736import qualified Data.Text as T
48−import Data.Tuple.Extra ((&&&))
49−import HscTypes
50−import Parser
5137import Text.Regex.TDFA ((=~), (=~~))
5238import Text.Regex.TDFA.Text()
5339import Outputable (ppr, showSDocUnsafe)
54−import DynFlags (xFlags, FlagSpec(..))
55−import GHC.LanguageExtensions.Type (Extension)
56−
57−plugin :: Plugin c
58−plugin = codeActionPluginWithRules rules codeAction <> Plugin mempty setHandlersCodeLens
59−
60−rules :: Rules ()
61−rules = rulePackageExports
6240
6341-- | Generate code actions.
6442codeAction
65− :: LSP.LspFuncs c
43+ :: LSP.LspFuncs ()
6644 -> IdeState
67− -> TextDocumentIdentifier
68− -> Range
69− -> CodeActionContext
70− -> IO (Either ResponseError [CAResult])
71−codeAction lsp state (TextDocumentIdentifier uri) _range CodeActionContext{_diagnostics=List xs} = do
45+ -> CodeActionParams
46+ -> IO (List CAResult)
47+codeAction lsp state CodeActionParams{_textDocument=TextDocumentIdentifier uri,_context=CodeActionContext{_diagnostics=List xs}} = do
7248 -- disable logging as its quite verbose
7349 -- logInfo (ideLogger ide) $ T.pack $ "Code action req: " ++ show arg
7450 contents <- LSP.getVirtualFileFunc lsp $ toNormalizedUri uri
7551 let text = Rope.toText . (_text :: VirtualFile -> Rope.Rope) <$> contents
76− mbFile = toNormalizedFilePath' <$> uriToFilePath uri
77− (ideOptions, parsedModule, join -> env) <- runAction state $
78− (,,) <$> getIdeOptions
79− <*> getParsedModule `traverse` mbFile
80− <*> use GhcSession `traverse` mbFile
81− pkgExports <- runAction state $ (useNoFile_ . PackageExports) `traverse` env
82− let dflags = hsc_dflags . hscEnv <$> env
83− pure $ Right
52+ (ideOptions, parsedModule) <- runAction state $
53+ (,) <$> getIdeOptions
54+ <*> (getParsedModule . toNormalizedFilePath) `traverse` uriToFilePath uri
55+ pure $ List
8456 [ CACodeAction $ CodeAction title (Just CodeActionQuickFix) (Just $ List [x]) (Just edit) Nothing
85− | x <- xs, (title, tedit) <- suggestAction dflags (fromMaybe mempty pkgExports) ideOptions ( join parsedModule ) text x
57+ | x <- xs, (title, tedit) <- suggestAction ideOptions ( join parsedModule ) text x
8658 , let edit = WorkspaceEdit (Just $ Map.singleton uri $ List tedit) Nothing
8759 ]
8860
8961-- | Generate code lenses.
9062codeLens
91− :: LSP.LspFuncs c
63+ :: LSP.LspFuncs ()
9264 -> IdeState
9365 -> CodeLensParams
94− -> IO (Either ResponseError (List CodeLens))
66+ -> IO (List CodeLens)
9567codeLens _lsp ideState CodeLensParams{_textDocument=TextDocumentIdentifier uri} = do
96− commandId <- makeLspCommandId "typesignature.add"
97− fmap (Right . List) $ case uriToFilePath' uri of
98− Just (toNormalizedFilePath' -> filePath) -> do
68+ case uriToFilePath' uri of
69+ Just (toNormalizedFilePath -> filePath) -> do
9970 _ <- runAction ideState $ runMaybeT $ useE TypeCheck filePath
10071 diag <- getDiagnostics ideState
10172 hDiag <- getHiddenDiagnostics ideState
102− pure
103− [ CodeLens _range (Just (Command title commandId (Just $ List [toJSON edit]))) Nothing
104− | (dFile, _, dDiag@Diagnostic{_range=_range}) <- diag ++ hDiag
73+ pure $ List
74+ [ CodeLens _range (Just (Command title "typesignature.add" (Just $ List [toJSON edit]))) Nothing
75+ | (dFile, _, dDiag@Diagnostic{_range=_range@Range{..},..}) <- diag ++ hDiag
10576 , dFile == filePath
10677 , (title, tedit) <- suggestSignature False dDiag
10778 , let edit = WorkspaceEdit (Just $ Map.singleton uri $ List tedit) Nothing
10879 ]
109− Nothing -> pure []
80+ Nothing -> pure $ List []
11081
11182-- | Execute the "typesignature.add" command.
11283executeAddSignatureCommand
113− :: LSP.LspFuncs c
84+ :: LSP.LspFuncs ()
11485 -> IdeState
11586 -> ExecuteCommandParams
116− -> IO (Either ResponseError Value, Maybe (ServerMethod, ApplyWorkspaceEditParams))
87+ -> IO (Value, Maybe (ServerMethod, ApplyWorkspaceEditParams))
11788executeAddSignatureCommand _lsp _ideState ExecuteCommandParams{..}
118− -- _command is prefixed with a process ID, because certain clients
119− -- have a global command registry, and all commands must be
120− -- unique. And there can be more than one ghcide instance running
121− -- at a time against the same client.
122− | T.isSuffixOf "typesignature.add" _command
89+ | _command == "typesignature.add"
12390 , Just (List [edit]) <- _arguments
124− , Success wedit <- fromJSON edit
125− = return (Right Null, Just (WorkspaceApplyEdit, ApplyWorkspaceEditParams wedit))
91+ , Success wedit <- fromJSON edit
92+ = return (Null, Just (WorkspaceApplyEdit, ApplyWorkspaceEditParams wedit))
12693 | otherwise
127− = return (Right Null, Nothing)
128−
129−suggestAction
130− :: Maybe DynFlags
131− -> PackageExportsMap
132− -> IdeOptions
133− -> Maybe ParsedModule
134− -> Maybe T.Text
135− -> Diagnostic
136− -> [(T.Text, [TextEdit])]
137−suggestAction dflags packageExports ideOptions parsedModule text diag = concat
94+ = return (Null, Nothing)
95+
96+suggestAction :: IdeOptions -> Maybe ParsedModule -> Maybe T.Text -> Diagnostic -> [(T.Text, [TextEdit])]
97+suggestAction ideOptions parsedModule text diag = concat
13898 [ suggestAddExtension diag
139− , suggestExtendImport dflags text diag
99+ , suggestExtendImport text diag
140100 , suggestFillHole diag
141101 , suggestFillTypeWildcard diag
142102 , suggestFixConstructorImport text diag
@@ -146,19 +106,17 @@ suggestAction dflags packageExports ideOptions parsedModule text diag = concat
146106 ] ++ concat
147107 [ suggestNewDefinition ideOptions pm text diag
148108 ++ suggestRemoveRedundantImport pm text diag
149− ++ suggestNewImport packageExports pm diag
150109 | Just pm <- [parsedModule]]
151110
152111
153112suggestRemoveRedundantImport :: ParsedModule -> Maybe T.Text -> Diagnostic -> [(T.Text, [TextEdit])]
154−suggestRemoveRedundantImport ParsedModule{pm_parsed_source = L _ HsModule{hsmodImports}} contents Diagnostic{_range=_range,..}
113+suggestRemoveRedundantImport ParsedModule{pm_parsed_source = L _ HsModule{hsmodImports}} contents Diagnostic{_range=_range@Range{..},..}
155114-- The qualified import of ‘many’ from module ‘Control.Applicative’ is redundant
156115 | Just [_, bindings] <- matchRegex _message "The( qualified)? import of ‘([^’]*)’ from module [^ ]* is redundant"
157116 , Just (L _ impDecl) <- find (\(L l _) -> srcSpanToRange l == _range ) hsmodImports
158117 , Just c <- contents
159118 , ranges <- map (rangesForBinding impDecl . T.unpack) (T.splitOn ", " bindings)
160119 , ranges' <- extendAllToIncludeCommaIfPossible (indexedByPosition $ T.unpack c) (concat ranges)
161− , not (null ranges')
162120 = [( "Remove " <> bindings <> " from import" , [ TextEdit r "" | r <- ranges' ] )]
163121
164122-- File.hs:16:1: warning:
@@ -170,7 +128,7 @@ suggestRemoveRedundantImport ParsedModule{pm_parsed_source = L _ HsModule{hsmod
170128 | otherwise = []
171129
172130suggestReplaceIdentifier :: Maybe T.Text -> Diagnostic -> [(T.Text, [TextEdit])]
173−suggestReplaceIdentifier contents Diagnostic{_range=_range,..}
131+suggestReplaceIdentifier contents Diagnostic{_range=_range@Range{..},..}
174132-- File.hs:52:41: error:
175133-- * Variable not in scope:
176134-- suggestAcion :: Maybe T.Text -> Range -> Range
@@ -217,7 +175,7 @@ newDefinitionAction IdeOptions{..} parsedModule Range{_start} name typ
217175
218176
219177suggestFillTypeWildcard :: Diagnostic -> [(T.Text, [TextEdit])]
220−suggestFillTypeWildcard Diagnostic{_range=_range,..}
178+suggestFillTypeWildcard Diagnostic{_range=_range@Range{..},..}
221179-- Foo.hs:3:8: error:
222180-- * Found type wildcard `_' standing for `p -> p1 -> p'
223181
@@ -228,7 +186,7 @@ suggestFillTypeWildcard Diagnostic{_range=_range,..}
228186 | otherwise = []
229187
230188suggestAddExtension :: Diagnostic -> [(T.Text, [TextEdit])]
231−suggestAddExtension Diagnostic{_range=_range,..}
189+suggestAddExtension Diagnostic{_range=_range@Range{..},..}
232190-- File.hs:22:8: error:
233191-- Illegal lambda-case (use -XLambdaCase)
234192-- File.hs:22:6: error:
@@ -249,16 +207,12 @@ suggestAddExtension Diagnostic{_range=_range,..}
249207-- * In the context: a ~ ()
250208-- While checking an instance declaration
251209-- In the instance declaration for `Unit (m a)'
252− | exts@(_:_) <- filter (`Map.member` ghcExtensions) $ T.split (not . isAlpha) $ T.replace "-X" "" _message
210+ | exts@(_:_) <- filter (`Set.member` ghcExtensions) $ T.split (not . isAlpha) $ T.replace "-X" "" _message
253211 = [("Add " <> x <> " extension", [TextEdit (Range (Position 0 0) (Position 0 0)) $ "{-# LANGUAGE " <> x <> " #-}\n"]) | x <- exts]
254212 | otherwise = []
255213
256−-- | All the GHC extensions
257−ghcExtensions :: Map.HashMap T.Text Extension
258−ghcExtensions = Map.fromList . map ( ( T.pack . flagSpecName ) &&& flagSpecFlag ) $ xFlags
259−
260214suggestModuleTypo :: Diagnostic -> [(T.Text, [TextEdit])]
261−suggestModuleTypo Diagnostic{_range=_range,..}
215+suggestModuleTypo Diagnostic{_range=_range@Range{..},..}
262216-- src/Development/IDE/Core/Compile.hs:58:1: error:
263217-- Could not find module ‘Data.Cha’
264218-- Perhaps you meant Data.Char (from base-4.12.0.0)
@@ -270,7 +224,7 @@ suggestModuleTypo Diagnostic{_range=_range,..}
270224 | otherwise = []
271225
272226suggestFillHole :: Diagnostic -> [(T.Text, [TextEdit])]
273−suggestFillHole Diagnostic{_range=_range,..}
227+suggestFillHole Diagnostic{_range=_range@Range{..},..}
274228-- ...Development/IDE/LSP/CodeAction.hs:103:9: warning:
275229-- * Found hole: _ :: Int -> String
276230-- * In the expression: _
@@ -305,22 +259,20 @@ suggestFillHole Diagnostic{_range=_range,..}
305259
306260 | otherwise = []
307261
308−suggestExtendImport :: Maybe DynFlags -> Maybe T.Text -> Diagnostic -> [(T.Text, [TextEdit])]
309−suggestExtendImport (Just dflags) contents Diagnostic{_range=_range,..}
262+suggestExtendImport :: Maybe T.Text -> Diagnostic -> [(T.Text, [TextEdit])]
263+suggestExtendImport contents Diagnostic{_range=_range,..}
310264 | Just [binding, mod, srcspan] <-
311265 matchRegex _message
312266 "Perhaps you want to add ‘([^’]*)’ to the import list in the import of ‘([^’]*)’ *\\((.*)\\).$"
313267 , Just c <- contents
314− , POk _ (L _ name) <- runParser dflags (T.unpack binding) parseIdentifier
315268 = let range = case [ x | (x,"") <- readSrcSpan (T.unpack srcspan)] of
316269 [s] -> let x = srcSpanToRange s
317270 in x{_end = (_end x){_character = succ (_character (_end x))}}
318271 _ -> error "bug in srcspan parser"
319272 importLine = textInRange range c
320273 in [("Add " <> binding <> " to the import list of " <> mod
321− , [TextEdit range (addBindingToImportList (T.pack $ printRdrName name) importLine)])]
274+ , [TextEdit range (addBindingToImportList binding importLine)])]
322275 | otherwise = []
323−suggestExtendImport Nothing _ _ = []
324276
325277suggestFixConstructorImport :: Maybe T.Text -> Diagnostic -> [(T.Text, [TextEdit])]
326278suggestFixConstructorImport _ Diagnostic{_range=_range,..}
@@ -338,103 +290,29 @@ suggestFixConstructorImport _ Diagnostic{_range=_range,..}
338290
339291suggestSignature :: Bool -> Diagnostic -> [(T.Text, [TextEdit])]
340292suggestSignature isQuickFix Diagnostic{_range=_range@Range{..},..}
341− | _message =~
342− ("(Top-level binding|Polymorphic local binding|Pattern synonym) with no type signature" :: T.Text) = let
293+ | "Top-level binding with no type signature" `T.isInfixOf` _message = let
294+ signature = T.strip $ unifySpaces $ last $ T.splitOn "type signature: " $ filterNewlines _message
295+ startOfLine = Position (_line _start) 0
296+ beforeLine = Range startOfLine startOfLine
297+ title = if isQuickFix then "add signature: " <> signature else signature
298+ action = TextEdit beforeLine $ signature <> "\n"
299+ in [(title, [action])]
300+suggestSignature isQuickFix Diagnostic{_range=_range@Range{..},..}
301+ | "Polymorphic local binding with no type signature" `T.isInfixOf` _message = let
343302 signature = removeInitialForAll
344303 $ T.takeWhile (\x -> x/='*' && x/='•')
345304 $ T.strip $ unifySpaces $ last $ T.splitOn "type signature: " $ filterNewlines _message
346− startOfLine = Position (_line _start) startCharacter
305+ startOfLine = Position (_line _start) (_character _start)
347306 beforeLine = Range startOfLine startOfLine
348307 title = if isQuickFix then "add signature: " <> signature else signature
349− action = TextEdit beforeLine $ signature <> "\n" <> T.replicate startCharacter " "
308+ action = TextEdit beforeLine $ signature <> "\n" <> T.replicate (_character _start) " "
350309 in [(title, [action])]
351310 where removeInitialForAll :: T.Text -> T.Text
352311 removeInitialForAll (T.breakOnEnd " :: " -> (nm, ty))
353312 | "forall" `T.isPrefixOf` ty = nm <> T.drop 2 (snd (T.breakOn "." ty))
354313 | otherwise = nm <> ty
355− startCharacter
356− | "Polymorphic local binding" `T.isPrefixOf` _message
357− = _character _start
358− | otherwise
359− = 0
360−
361314suggestSignature _ _ = []
362315
363−-------------------------------------------------------------------------------------------------
364−
365−suggestNewImport :: PackageExportsMap -> ParsedModule -> Diagnostic -> [(T.Text, [TextEdit])]
366−suggestNewImport packageExportsMap ParsedModule {pm_parsed_source = L _ HsModule {..}} Diagnostic{_message}
367− | msg <- unifySpaces _message
368− , Just name <- extractNotInScopeName msg
369− , Just insertLine <- case hsmodImports of
370− [] -> case srcSpanStart $ getLoc (head hsmodDecls) of
371− RealSrcLoc s -> Just $ srcLocLine s - 1
372− _ -> Nothing
373− _ -> case srcSpanEnd $ getLoc (last hsmodImports) of
374− RealSrcLoc s -> Just $ srcLocLine s
375− _ -> Nothing
376− , insertPos <- Position insertLine 0
377− , extendImportSuggestions <- matchRegex msg
378− "Perhaps you want to add ‘[^’]*’ to the import list in the import of ‘([^’]*)’"
379− = [(imp, [TextEdit (Range insertPos insertPos) (imp <> "\n")])
380− | imp <- constructNewImportSuggestions packageExportsMap name extendImportSuggestions
381− ]
382−suggestNewImport _ _ _ = []
383−
384−constructNewImportSuggestions
385− :: PackageExportsMap -> NotInScope -> Maybe [T.Text] -> [T.Text]
386−constructNewImportSuggestions exportsMap thingMissing notTheseModules = nubOrd
387− [ renderNewImport identInfo m
388− | (identInfo, m) <- fromMaybe [] $ Map.lookup name exportsMap
389− , canUseIdent thingMissing identInfo
390− , m `notElem` fromMaybe [] notTheseModules
391− ]
392− where
393− renderNewImport identInfo m
394− | Just q <- qual = "import qualified " <> m <> " as " <> q
395− | otherwise = "import " <> m <> " (" <> importWhat identInfo <> ")"
396−
397− (qual, name) = case T.splitOn "." (notInScope thingMissing) of
398− [n] -> (Nothing, n)
399− segments -> (Just (T.concat $ init segments), last segments)
400− importWhat IdentInfo {parent, rendered}
401− | Just p <- parent = p <> "(" <> rendered <> ")"
402− | otherwise = rendered
403−
404−canUseIdent :: NotInScope -> IdentInfo -> Bool
405−canUseIdent NotInScopeDataConstructor{} = isDatacon
406−canUseIdent _ = const True
407−
408−data NotInScope
409− = NotInScopeDataConstructor T.Text
410− | NotInScopeTypeConstructorOrClass T.Text
411− | NotInScopeThing T.Text
412− deriving Show
413−
414−notInScope :: NotInScope -> T.Text
415−notInScope (NotInScopeDataConstructor t) = t
416−notInScope (NotInScopeTypeConstructorOrClass t) = t
417−notInScope (NotInScopeThing t) = t
418−
419−extractNotInScopeName :: T.Text -> Maybe NotInScope
420−extractNotInScopeName x
421− | Just [name] <- matchRegex x "Data constructor not in scope: ([^ ]+)"
422− = Just $ NotInScopeDataConstructor name
423− | Just [name] <- matchRegex x "Not in scope: data constructor [^‘]*‘([^’]*)’"
424− = Just $ NotInScopeDataConstructor name
425− | Just [name] <- matchRegex x "ot in scope: type constructor or class [^‘]*‘([^’]*)’"
426− = Just $ NotInScopeTypeConstructorOrClass name
427− | Just [name] <- matchRegex x "ot in scope: \\(([^‘ ]+)\\)"
428− = Just $ NotInScopeThing name
429− | Just [name] <- matchRegex x "ot in scope: ([^‘ ]+)"
430− = Just $ NotInScopeThing name
431− | Just [name] <- matchRegex x "ot in scope:[^‘]*‘([^’]*)’"
432− = Just $ NotInScopeThing name
433− | otherwise
434− = Nothing
435−
436−-------------------------------------------------------------------------------------------------
437−
438316topOfHoleFitsMarker :: T.Text
439317topOfHoleFitsMarker =
440318#if MIN_GHC_API_VERSION(8,6,0)
@@ -445,7 +323,7 @@ topOfHoleFitsMarker =
445323
446324mkRenameEdit :: Maybe T.Text -> Range -> T.Text -> TextEdit
447325mkRenameEdit contents range name =
448− if maybeIsInfixFunction == Just True
326+ if fromMaybe False maybeIsInfixFunction
449327 then TextEdit range ("`" <> name <> "`")
450328 else TextEdit range name
451329 where
@@ -486,6 +364,10 @@ extendToWholeLineIfPossible contents range@Range{..} =
486364 extend = newlineAfter && _character _start == 0 -- takes up an entire line, so remove the whole line
487365 in if extend then Range _start (Position (_line _end + 1) 0) else range
488366
367+-- | All the GHC extensions
368+ghcExtensions :: Set.HashSet T.Text
369+ghcExtensions = Set.fromList $ map (T.pack . show) ghcEnumerateExtensions
370+
489371splitTextAtPosition :: Position -> T.Text -> (T.Text, T.Text)
490372splitTextAtPosition (Position row col) x
491373 | (preRow, mid:postRow) <- splitAt row $ T.splitOn "\n" x
@@ -527,7 +409,7 @@ rangesForBinding _ _ = []
527409rangesForBinding' :: String -> LIE GhcPs -> [SrcSpan]
528410rangesForBinding' b (L l x@IEVar{}) | showSDocUnsafe (ppr x) == b = [l]
529411rangesForBinding' b (L l x@IEThingAbs{}) | showSDocUnsafe (ppr x) == b = [l]
530−rangesForBinding' b (L l (IEThingAll x)) | showSDocUnsafe (ppr x) == b = [l]
412+rangesForBinding' b (L l x@IEThingAll{}) | showSDocUnsafe (ppr x) == b = [l]
531413rangesForBinding' b (L l (IEThingWith thing _ inners labels))
532414 | showSDocUnsafe (ppr thing) == b = [l]
533415 | otherwise =
@@ -557,7 +439,12 @@ matchRegex message regex = case unifySpaces message =~~ regex of
557439 Just (_ :: T.Text, _ :: T.Text, _ :: T.Text, bindings) -> Just bindings
558440 Nothing -> Nothing
559441
560−setHandlersCodeLens :: PartialHandlers c
442+setHandlersCodeAction :: PartialHandlers
443+setHandlersCodeAction = PartialHandlers $ \WithMessage{..} x -> return x{
444+ LSP.codeActionHandler = withResponse RspCodeAction codeAction
445+ }
446+
447+setHandlersCodeLens :: PartialHandlers
561448setHandlersCodeLens = PartialHandlers $ \WithMessage{..} x -> return x{
562449 LSP.codeLensHandler = withResponse RspCodeLens codeLens,
563450 LSP.executeCommandHandler = withResponseAndRequest RspExecuteCommand ReqApplyWorkspaceEdit executeAddSignatureCommand
@@ -568,3 +455,51 @@ filterNewlines = T.concat . T.lines
568455
569456unifySpaces :: T.Text -> T.Text
570457unifySpaces = T.unwords . T.words
458+
459+--------------------------------------------------------------------------------
460+
461+type PositionIndexedString = [(Position, Char)]
462+
463+indexedByPosition :: String -> PositionIndexedString
464+indexedByPosition = unfoldr f . (Position 0 0,) where
465+ f (_, []) = Nothing
466+ f (p@(Position l _), '\n' : rest) = Just ((p,'\n'), (Position (l+1) 0, rest))
467+ f (p@(Position l c), x : rest) = Just ((p, x), (Position l (c+1), rest))
468+
469+-- | Returns a tuple (before, contents, after)
470+unconsRange :: Range -> PositionIndexedString -> (PositionIndexedString, PositionIndexedString, PositionIndexedString)
471+unconsRange Range {..} indexedString = (before, mid, after)
472+ where
473+ (before, rest) = span ((/= _start) . fst) indexedString
474+ (mid, after) = span ((/= _end) . fst) rest
475+
476+stripRange :: Range -> PositionIndexedString -> PositionIndexedString
477+stripRange r s = case unconsRange r s of
478+ (b, _, a) -> b ++ a
479+
480+extendAllToIncludeCommaIfPossible :: PositionIndexedString -> [Range] -> [Range]
481+extendAllToIncludeCommaIfPossible _ [] = []
482+extendAllToIncludeCommaIfPossible indexedString (r : rr) = r' : extendAllToIncludeCommaIfPossible indexedString' rr
483+ where
484+ r' = case extendToIncludeCommaIfPossible indexedString r of
485+ [] -> r
486+ r' : _ -> r'
487+ indexedString' = stripRange r' indexedString
488+
489+-- | Returns a sorted list of ranges with extended selections includindg preceding or trailing commas
490+extendToIncludeCommaIfPossible :: PositionIndexedString -> Range -> [Range]
491+extendToIncludeCommaIfPossible indexedString range =
492+ -- a, |b|, c ===> a|, b|, c
493+ [ range{_start = start'}
494+ | (start', ',') : _ <- [before']
495+ ]
496+ ++
497+ -- a, |b|, c ===> a, |b, |c
498+ [ range{_end = end'}
499+ | (_, ',') : rest <- [after']
500+ , let (end', _) : _ = dropWhile (isSpace . snd) rest
501+ ]
502+ where
503+ (before, _, after) = unconsRange range indexedString
504+ after' = dropWhile (isSpace . snd) after
505+ before' = dropWhile (isSpace . snd) (reverse before)
src/Development/IDE/LSP/Completions.hsnew file+51
@@ -0,0 +1,51 @@
1+module Development.IDE.LSP.Completions (
2+ setHandlersCompletion
3+) where
4+
5+import Language.Haskell.LSP.Messages
6+import Language.Haskell.LSP.Types
7+import qualified Language.Haskell.LSP.Core as LSP
8+import qualified Language.Haskell.LSP.VFS as VFS
9+import Language.Haskell.LSP.Types.Capabilities
10+
11+import Development.IDE.Core.Service
12+import Development.IDE.Core.Completions
13+import Development.IDE.Types.Location
14+import Development.IDE.Core.PositionMapping
15+import Development.IDE.Core.RuleTypes
16+import Development.IDE.Core.Shake
17+import Development.IDE.LSP.Server
18+
19+-- | Generate code actions.
20+getCompletionsLSP
21+ :: LSP.LspFuncs ()
22+ -> IdeState
23+ -> CompletionParams
24+ -> IO CompletionResponseResult
25+getCompletionsLSP lsp ide
26+ CompletionParams{_textDocument=TextDocumentIdentifier uri
27+ ,_position=position
28+ ,_context=completionContext} = do
29+ contents <- LSP.getVirtualFileFunc lsp $ toNormalizedUri uri
30+ case (contents, uriToFilePath' uri) of
31+ (Just cnts, Just path) -> do
32+ let npath = toNormalizedFilePath path
33+ (ideOpts, compls) <- runAction ide ((,) <$> getIdeOptions <*> useWithStale ProduceCompletions npath)
34+ case compls of
35+ Just ((cci', tm'), mapping) -> do
36+ let position' = fromCurrentPosition mapping position
37+ pfix <- maybe (return Nothing) (flip VFS.getCompletionPrefix cnts) position'
38+ case (pfix, completionContext) of
39+ (Just (VFS.PosPrefixInfo _ "" _ _), Just CompletionContext { _triggerCharacter = Just "."})
40+ -> return (Completions $ List [])
41+ (Just pfix', _) -> do
42+ let fakeClientCapabilities = ClientCapabilities Nothing Nothing Nothing Nothing
43+ Completions . List <$> getCompletions ideOpts cci' (tmrModule tm') pfix' fakeClientCapabilities (WithSnippets True)
44+ _ -> return (Completions $ List [])
45+ _ -> return (Completions $ List [])
46+ _ -> return (Completions $ List [])
47+
48+setHandlersCompletion :: PartialHandlers
49+setHandlersCompletion = PartialHandlers $ \WithMessage{..} x -> return x{
50+ LSP.completionHandler = withResponse RspCompletion getCompletionsLSP
51+ }
src/Development/IDE/LSP/HoverDefinition.hs+6−9
@@ -6,9 +6,6 @@
66module Development.IDE.LSP.HoverDefinition
77 ( setHandlersHover
88 , setHandlersDefinition
9− -- * For haskell-language-server
10− , hover
11− , gotoDefinition
129 ) where
1310
1411import Development.IDE.Core.Rules
@@ -23,8 +20,8 @@ import Language.Haskell.LSP.Types
2320
2421import qualified Data.Text as T
2522
26−gotoDefinition :: IdeState -> TextDocumentPositionParams -> IO (Either ResponseError LocationResponseParams)
27−hover :: IdeState -> TextDocumentPositionParams -> IO (Either ResponseError (Maybe Hover))
23+gotoDefinition :: IdeState -> TextDocumentPositionParams -> IO LocationResponseParams
24+hover :: IdeState -> TextDocumentPositionParams -> IO (Maybe Hover)
2825gotoDefinition = request "Definition" getDefinition (MultiLoc []) SingleLoc
2926hover = request "Hover" getAtPoint Nothing foundHover
3027
@@ -32,7 +29,7 @@ foundHover :: (Maybe Range, [T.Text]) -> Maybe Hover
3229foundHover (mbRange, contents) =
3330 Just $ Hover (HoverContents $ MarkupContent MkMarkdown $ T.intercalate sectionSeparator contents) mbRange
3431
35−setHandlersDefinition, setHandlersHover :: PartialHandlers c
32+setHandlersDefinition, setHandlersHover :: PartialHandlers
3633setHandlersDefinition = PartialHandlers $ \WithMessage{..} x ->
3734 return x{LSP.definitionHandler = withResponse RspDefinition $ const gotoDefinition}
3835setHandlersHover = PartialHandlers $ \WithMessage{..} x ->
@@ -46,16 +43,16 @@ request
4643 -> (a -> b)
4744 -> IdeState
4845 -> TextDocumentPositionParams
49− -> IO (Either ResponseError b)
46+ -> IO b
5047request label getResults notFound found ide (TextDocumentPositionParams (TextDocumentIdentifier uri) pos _) = do
5148 mbResult <- case uriToFilePath' uri of
5249 Just path -> logAndRunRequest label getResults ide pos path
5350 Nothing -> pure Nothing
54− pure $ Right $ maybe notFound found mbResult
51+ pure $ maybe notFound found mbResult
5552
5653logAndRunRequest :: T.Text -> (NormalizedFilePath -> Position -> Action b) -> IdeState -> Position -> String -> IO b
5754logAndRunRequest label getResults ide pos path = do
58− let filePath = toNormalizedFilePath' path
55+ let filePath = toNormalizedFilePath path
5956 logInfo (ideLogger ide) $
6057 label <> " request at position " <> T.pack (showPosition pos) <>
6158 " in file: " <> T.pack path
src/Development/IDE/LSP/LanguageServer.hs+32−54
@@ -28,25 +28,23 @@ import GHC.IO.Handle (hDuplicate)
2828import System.IO
2929import Control.Monad.Extra
3030
31−import Development.IDE.Core.IdeConfiguration
32−import Development.IDE.Core.Shake
3331import Development.IDE.LSP.HoverDefinition
32+import Development.IDE.LSP.CodeAction
33+import Development.IDE.LSP.Completions
3434import Development.IDE.LSP.Notifications
3535import Development.IDE.LSP.Outline
36+import Development.IDE.Core.Service
3637import Development.IDE.Types.Logger
3738import Development.IDE.Core.FileStore
3839import Language.Haskell.LSP.Core (LspFuncs(..))
3940import Language.Haskell.LSP.Messages
4041
4142runLanguageServer
42− :: forall config. (Show config)
43− => LSP.Options
44− -> PartialHandlers config
45− -> (InitializeRequest -> Either T.Text config)
46− -> (DidChangeConfigurationNotification -> Either T.Text config)
43+ :: LSP.Options
44+ -> PartialHandlers
4745 -> (IO LspId -> (FromServerMessage -> IO ()) -> VFSHandle -> ClientCapabilities -> IO IdeState)
4846 -> IO ()
49−runLanguageServer options userHandlers onInitialConfig onConfigChange getIdeState = do
47+runLanguageServer options userHandlers getIdeState = do
5048 -- Move stdout to another file descriptor and duplicate stderr
5149 -- to stdout. This guards against stray prints from corrupting the JSON-RPC
5250 -- message stream.
@@ -63,7 +61,7 @@ runLanguageServer options userHandlers onInitialConfig onConfigChange getIdeStat
6361
6462 -- Send everything over a channel, since you need to wait until after initialise before
6563 -- LspFuncs is available
66− clientMsgChan :: Chan (Message config) <- newChan
64+ clientMsgChan :: Chan Message <- newChan
6765
6866 -- These barriers are signaled when the threads reading from these chans exit.
6967 -- This should not happen but if it does, we will make sure that the whole server
@@ -82,7 +80,6 @@ runLanguageServer options userHandlers onInitialConfig onConfigChange getIdeStat
8280 let withResponseAndRequest wrap wrapNewReq f = Just $ \r@RequestMessage{_id} -> do
8381 atomically $ modifyTVar pendingRequests (Set.insert _id)
8482 writeChan clientMsgChan $ ResponseAndRequest r wrap wrapNewReq f
85− let withInitialize f = Just $ \r -> writeChan clientMsgChan $ InitialParams r (\lsp ide x -> f lsp ide x)
8683 let cancelRequest reqId = atomically $ do
8784 queued <- readTVar pendingRequests
8885 -- We want to avoid that the list of cancelled requests
@@ -99,20 +96,21 @@ runLanguageServer options userHandlers onInitialConfig onConfigChange getIdeStat
9996 cancelled <- readTVar cancelledRequests
10097 unless (reqId `Set.member` cancelled) retry
10198 let PartialHandlers parts =
102− initializeRequestHandler <>
10399 setHandlersIgnore <> -- least important
104100 setHandlersDefinition <> setHandlersHover <>
101+ setHandlersCodeAction <> setHandlersCodeLens <> -- useful features someone may override
102+ setHandlersCompletion <>
105103 setHandlersOutline <>
106104 userHandlers <>
107105 setHandlersNotifications <> -- absolutely critical, join them with user notifications
108106 cancelHandler cancelRequest
109107 -- Cancel requests are special since they need to be handled
110108 -- out of order to be useful. Existing handlers are run afterwards.
111− handlers <- parts WithMessage{withResponse, withNotification, withResponseAndRequest, withInitialize} def
109+ handlers <- parts WithMessage{withResponse, withNotification, withResponseAndRequest} def
112110
113111 let initializeCallbacks = LSP.InitializeCallbacks
114− { LSP.onInitialConfiguration = onInitialConfig
115− , LSP.onConfigurationChange = onConfigChange
112+ { LSP.onInitialConfiguration = const $ Right ()
113+ , LSP.onConfigurationChange = const $ Right ()
116114 , LSP.onStartup = handleInit (signalBarrier clientMsgBarrier ()) clearReqId waitForCancel clientMsgChan
117115 }
118116
@@ -127,11 +125,9 @@ runLanguageServer options userHandlers onInitialConfig onConfigChange getIdeStat
127125 , void $ waitBarrier clientMsgBarrier
128126 ]
129127 where
130− handleInit :: IO () -> (LspId -> IO ()) -> (LspId -> IO ()) -> Chan (Message config) -> LSP.LspFuncs config -> IO (Maybe err)
128+ handleInit :: IO () -> (LspId -> IO ()) -> (LspId -> IO ()) -> Chan Message -> LSP.LspFuncs () -> IO (Maybe err)
131129 handleInit exitClientMsg clearReqId waitForCancel clientMsgChan lspFuncs@LSP.LspFuncs{..} = do
132−
133130 ide <- getIdeState getNextReqId sendFunc (makeLSPVFSHandle lspFuncs) clientCapabilities
134−
135131 _ <- flip forkFinally (const exitClientMsg) $ forever $ do
136132 msg <- readChan clientMsgChan
137133 case msg of
@@ -142,25 +138,17 @@ runLanguageServer options userHandlers onInitialConfig onConfigChange getIdeStat
142138 "Message: " ++ show x ++ "\n" ++
143139 "Exception: " ++ show e
144140 Response x@RequestMessage{_id, _params} wrap act ->
145− checkCancelled ide clearReqId waitForCancel lspFuncs wrap act x _id _params $
146− \case
147− Left e -> sendFunc $ wrap $ ResponseMessage "2.0" (responseId _id) (Left e)
148− Right r -> sendFunc $ wrap $ ResponseMessage "2.0" (responseId _id) (Right r)
141+ checkCancelled ide clearReqId waitForCancel lspFuncs wrap act x _id _params $
142+ \res -> sendFunc $ wrap $ ResponseMessage "2.0" (responseId _id) (Just res) Nothing
149143 ResponseAndRequest x@RequestMessage{_id, _params} wrap wrapNewReq act ->
150144 checkCancelled ide clearReqId waitForCancel lspFuncs wrap act x _id _params $
151145 \(res, newReq) -> do
152− case res of
153− Left e -> sendFunc $ wrap $ ResponseMessage "2.0" (responseId _id) (Left e)
154− Right r -> sendFunc $ wrap $ ResponseMessage "2.0" (responseId _id) (Right r)
155− whenJust newReq $ \(rm, newReqParams) -> do
146+ sendFunc $ wrap $ ResponseMessage "2.0" (responseId _id) (Just res) Nothing
147+ case newReq of
148+ Nothing -> return ()
149+ Just (rm, newReqParams) -> do
156150 reqId <- getNextReqId
157151 sendFunc $ wrapNewReq $ RequestMessage "2.0" reqId rm newReqParams
158− InitialParams x@RequestMessage{_id, _params} act -> do
159− catch (act lspFuncs ide _params) $ \(e :: SomeException) ->
160− logError (ideLogger ide) $ T.pack $
161− "Unexpected exception on InitializeRequest handler, please report!\n" ++
162− "Message: " ++ show x ++ "\n" ++
163− "Exception: " ++ show e
164152 pure Nothing
165153
166154 checkCancelled ide clearReqId waitForCancel lspFuncs@LSP.LspFuncs{..} wrap act msg _id _params k =
@@ -175,39 +163,28 @@ runLanguageServer options userHandlers onInitialConfig onConfigChange getIdeStat
175163 Left () -> do
176164 logDebug (ideLogger ide) $ T.pack $
177165 "Cancelled request " <> show _id
178− sendFunc $ wrap $ ResponseMessage "2.0" (responseId _id) $ Left
179− $ ResponseError RequestCancelled "" Nothing
166+ sendFunc $ wrap $ ResponseMessage "2.0" (responseId _id) Nothing $
167+ Just $ ResponseError RequestCancelled "" Nothing
180168 Right res -> k res
181169 ) $ \(e :: SomeException) -> do
182170 logError (ideLogger ide) $ T.pack $
183171 "Unexpected exception on request, please report!\n" ++
184172 "Message: " ++ show msg ++ "\n" ++
185173 "Exception: " ++ show e
186− sendFunc $ wrap $ ResponseMessage "2.0" (responseId _id) $ Left
187− $ ResponseError InternalError (T.pack $ show e) Nothing
174+ sendFunc $ wrap $ ResponseMessage "2.0" (responseId _id) Nothing $
175+ Just $ ResponseError InternalError (T.pack $ show e) Nothing
188176
189−initializeRequestHandler :: PartialHandlers config
190−initializeRequestHandler = PartialHandlers $ \WithMessage{..} x -> return x{
191− LSP.initializeRequestHandler = withInitialize initHandler
192− }
193−
194−initHandler
195− :: LSP.LspFuncs c
196− -> IdeState
197− -> InitializeParams
198− -> IO ()
199−initHandler _ ide params = registerIdeConfiguration (shakeExtras ide) (parseConfiguration params)
200177
201178-- | Things that get sent to us, but we don't deal with.
202179-- Set them to avoid a warning in VS Code output.
203−setHandlersIgnore :: PartialHandlers config
180+setHandlersIgnore :: PartialHandlers
204181setHandlersIgnore = PartialHandlers $ \_ x -> return x
205182 {LSP.initializedHandler = none
206183 ,LSP.responseHandler = none
207184 }
208185 where none = Just $ const $ return ()
209186
210−cancelHandler :: (LspId -> IO ()) -> PartialHandlers config
187+cancelHandler :: (LspId -> IO ()) -> PartialHandlers
211188cancelHandler cancelRequest = PartialHandlers $ \_ x -> return x
212189 {LSP.cancelNotificationHandler = Just $ \msg@NotificationMessage {_params = CancelParams {_id}} -> do
213190 cancelRequest _id
@@ -217,18 +194,19 @@ cancelHandler cancelRequest = PartialHandlers $ \_ x -> return x
217194
218195-- | A message that we need to deal with - the pieces are split up with existentials to gain additional type safety
219196-- and defer precise processing until later (allows us to keep at a higher level of abstraction slightly longer)
220−data Message c
221− = forall m req resp . (Show m, Show req) => Response (RequestMessage m req resp) (ResponseMessage resp -> FromServerMessage) (LSP.LspFuncs c -> IdeState -> req -> IO (Either ResponseError resp))
197+data Message
198+ = forall m req resp . (Show m, Show req) => Response (RequestMessage m req resp) (ResponseMessage resp -> FromServerMessage) (LSP.LspFuncs () -> IdeState -> req -> IO resp)
222199 -- | Used for cases in which we need to send not only a response,
223200 -- but also an additional request to the client.
224201 -- For example, 'executeCommand' may generate an 'applyWorkspaceEdit' request.
225− | forall m rm req resp newReqParams newReqBody . (Show m, Show rm, Show req) => ResponseAndRequest (RequestMessage m req resp) (ResponseMessage resp -> FromServerMessage) (RequestMessage rm newReqParams newReqBody -> FromServerMessage) (LSP.LspFuncs c -> IdeState -> req -> IO (Either ResponseError resp, Maybe (rm, newReqParams)))
226− | forall m req . (Show m, Show req) => Notification (NotificationMessage m req) (LSP.LspFuncs c -> IdeState -> req -> IO ())
227− -- | Used for the InitializeRequest only, where the response is generated by the LSP core handler.
228− | InitialParams InitializeRequest (LSP.LspFuncs c -> IdeState -> InitializeParams -> IO ())
202+ | forall m rm req resp newReqParams newReqBody . (Show m, Show rm, Show req) => ResponseAndRequest (RequestMessage m req resp) (ResponseMessage resp -> FromServerMessage) (RequestMessage rm newReqParams newReqBody -> FromServerMessage) (LSP.LspFuncs () -> IdeState -> req -> IO (resp, Maybe (rm, newReqParams)))
203+ | forall m req . (Show m, Show req) => Notification (NotificationMessage m req) (LSP.LspFuncs () -> IdeState -> req -> IO ())
204+
229205
230206modifyOptions :: LSP.Options -> LSP.Options
231207modifyOptions x = x{ LSP.textDocumentSync = Just $ tweakTDS origTDS
208+ , LSP.executeCommandCommands = Just ["typesignature.add"]
209+ , LSP.completionTriggerCharacters = Just "."
232210 }
233211 where
234212 tweakTDS tds = tds{_openClose=Just True, _change=Just TdSyncIncremental, _save=Just $ SaveOptions Nothing}
src/Development/IDE/LSP/Notifications.hs+5−14
@@ -13,7 +13,6 @@ import qualified Language.Haskell.LSP.Core as LSP
1313import Language.Haskell.LSP.Types
1414import qualified Language.Haskell.LSP.Types as LSP
1515
16−import Development.IDE.Core.IdeConfiguration
1716import Development.IDE.Core.Service
1817import Development.IDE.Types.Location
1918import Development.IDE.Types.Logger
@@ -21,7 +20,7 @@ import Development.IDE.Types.Logger
2120import Control.Monad.Extra
2221import Data.Foldable as F
2322import Data.Maybe
24−import qualified Data.HashSet as S
23+import qualified Data.Set as S
2524import qualified Data.Text as Text
2625
2726import Development.IDE.Core.FileStore (setSomethingModified)
@@ -30,9 +29,9 @@ import Development.IDE.Core.OfInterest
3029
3130
3231whenUriFile :: Uri -> (NormalizedFilePath -> IO ()) -> IO ()
33−whenUriFile uri act = whenJust (LSP.uriToFilePath uri) $ act . toNormalizedFilePath'
32+whenUriFile uri act = whenJust (LSP.uriToFilePath uri) $ act . toNormalizedFilePath
3433
35−setHandlersNotifications :: PartialHandlers c
34+setHandlersNotifications :: PartialHandlers
3635setHandlersNotifications = PartialHandlers $ \WithMessage{..} x -> return x
3736 {LSP.didOpenTextDocumentNotificationHandler = withNotification (LSP.didOpenTextDocumentNotificationHandler x) $
3837 \_ ide (DidOpenTextDocumentParams TextDocumentItem{_uri,_version}) -> do
@@ -62,7 +61,7 @@ setHandlersNotifications = PartialHandlers $ \WithMessage{..} x -> return x
6261 let events =
6362 mapMaybe
6463 (\(FileEvent uri ev) ->
65− (, ev /= FcDeleted) . toNormalizedFilePath'
64+ (, ev /= FcDeleted) . toNormalizedFilePath
6665 <$> LSP.uriToFilePath uri
6766 )
6867 ( F.toList fileEvents )
@@ -70,12 +69,4 @@ setHandlersNotifications = PartialHandlers $ \WithMessage{..} x -> return x
7069 logInfo (ideLogger ide) $ "Files created or deleted: " <> msg
7170 modifyFileExists ide events
7271 setSomethingModified ide
73−
74− ,LSP.didChangeWorkspaceFoldersNotificationHandler = withNotification (LSP.didChangeWorkspaceFoldersNotificationHandler x) $
75− \_ ide (DidChangeWorkspaceFoldersParams events) -> do
76− let add = S.union
77− substract = flip S.difference
78− modifyWorkspaceFolders ide
79− $ add (foldMap (S.singleton . parseWorkspaceFolder) (_added events))
80− . substract (foldMap (S.singleton . parseWorkspaceFolder) (_removed events))
81− }
72+ }
src/Development/IDE/LSP/Outline.hs+10−49
@@ -4,8 +4,6 @@
44
55module Development.IDE.LSP.Outline
66 ( setHandlersOutline
7− -- * For haskell-language-server
8− , moduleOutline
97 )
108where
119
@@ -30,20 +28,20 @@ import Outputable ( Outputable
3028 , showSDocUnsafe
3129 )
3230
33−setHandlersOutline :: PartialHandlers c
31+setHandlersOutline :: PartialHandlers
3432setHandlersOutline = PartialHandlers $ \WithMessage {..} x -> return x
3533 { LSP.documentSymbolHandler = withResponse RspDocumentSymbols moduleOutline
3634 }
3735
3836moduleOutline
39− :: LSP.LspFuncs c -> IdeState -> DocumentSymbolParams -> IO (Either ResponseError DSResult)
37+ :: LSP.LspFuncs () -> IdeState -> DocumentSymbolParams -> IO DSResult
4038moduleOutline _lsp ideState DocumentSymbolParams { _textDocument = TextDocumentIdentifier uri }
4139 = case uriToFilePath uri of
42− Just (toNormalizedFilePath' -> fp) -> do
40+ Just (toNormalizedFilePath -> fp) -> do
4341 mb_decls <- runAction ideState $ use GetParsedModule fp
44− pure $ Right $ case mb_decls of
42+ pure $ case mb_decls of
4543 Nothing -> DSDocumentSymbols (List [])
46− Just ParsedModule { pm_parsed_source = L _ltop HsModule { hsmodName, hsmodDecls, hsmodImports } }
44+ Just (ParsedModule { pm_parsed_source = L _ltop HsModule { hsmodName, hsmodDecls, hsmodImports } })
4745 -> let
4846 declSymbols = mapMaybe documentSymbolForDecl hsmodDecls
4947 moduleSymbol = hsmodName <&> \(L l m) ->
@@ -52,9 +50,7 @@ moduleOutline _lsp ideState DocumentSymbolParams { _textDocument = TextDocumentI
5250 , _kind = SkFile
5351 , _range = Range (Position 0 0) (Position maxBound 0) -- _ltop is 0 0 0 0
5452 }
55− importSymbols = maybe [] pure $
56− documentSymbolForImportSummary
57− (mapMaybe documentSymbolForImport hsmodImports)
53+ importSymbols = mapMaybe documentSymbolForImport hsmodImports
5854 allSymbols = case moduleSymbol of
5955 Nothing -> importSymbols <> declSymbols
6056 Just x ->
@@ -65,7 +61,7 @@ moduleOutline _lsp ideState DocumentSymbolParams { _textDocument = TextDocumentI
6561 DSDocumentSymbols (List allSymbols)
6662
6763
68− Nothing -> pure $ Right $ DSDocumentSymbols (List [])
64+ Nothing -> pure $ DSDocumentSymbols (List [])
6965
7066documentSymbolForDecl :: Located (HsDecl GhcPs) -> Maybe DocumentSymbol
7167documentSymbolForDecl (L l (TyClD FamDecl { tcdFam = FamilyDecl { fdLName = L _ n, fdInfo, fdTyVars } }))
@@ -108,39 +104,27 @@ documentSymbolForDecl (L l (TyClD DataDecl { tcdLName = L _ name, tcdDataDefn =
108104 { _name = showRdrName n
109105 , _kind = SkConstructor
110106 , _selectionRange = srcSpanToRange l'
111− , _children = conArgRecordFields (getConArgs x)
112107 }
113108 | L l x <- dd_cons
114109 , L l' n <- getConNames x
115110 ]
116111 }
117− where
118− -- | Extract the record fields of a constructor
119− conArgRecordFields (RecCon (L _ lcdfs)) = Just $ List
120− [ (defDocumentSymbol l :: DocumentSymbol)
121− { _name = showRdrName n
122− , _kind = SkField
123− }
124− | L _ cdf <- lcdfs
125− , L l n <- rdrNameFieldOcc . unLoc <$> cd_fld_names cdf
126− ]
127− conArgRecordFields _ = Nothing
128112documentSymbolForDecl (L l (TyClD SynDecl { tcdLName = L l' n })) = Just
129113 (defDocumentSymbol l :: DocumentSymbol) { _name = showRdrName n
130114 , _kind = SkTypeParameter
131115 , _selectionRange = srcSpanToRange l'
132116 }
133−documentSymbolForDecl (L l (InstD ClsInstD { cid_inst = ClsInstDecl { cid_poly_ty } }))
117+documentSymbolForDecl (L l (InstD (ClsInstD { cid_inst = ClsInstDecl { cid_poly_ty } })))
134118 = Just (defDocumentSymbol l :: DocumentSymbol) { _name = pprText cid_poly_ty
135119 , _kind = SkInterface
136120 }
137−documentSymbolForDecl (L l (InstD DataFamInstD { dfid_inst = DataFamInstDecl HsIB { hsib_body = FamEqn { feqn_tycon, feqn_pats } } }))
121+documentSymbolForDecl (L l (InstD DataFamInstD { dfid_inst = DataFamInstDecl (HsIB { hsib_body = FamEqn { feqn_tycon, feqn_pats } }) }))
138122 = Just (defDocumentSymbol l :: DocumentSymbol)
139123 { _name = showRdrName (unLoc feqn_tycon) <> " " <> T.unwords
140124 (map pprText feqn_pats)
141125 , _kind = SkInterface
142126 }
143−documentSymbolForDecl (L l (InstD TyFamInstD { tfid_inst = TyFamInstDecl HsIB { hsib_body = FamEqn { feqn_tycon, feqn_pats } } }))
127+documentSymbolForDecl (L l (InstD TyFamInstD { tfid_inst = TyFamInstDecl (HsIB { hsib_body = FamEqn { feqn_tycon, feqn_pats } }) }))
144128 = Just (defDocumentSymbol l :: DocumentSymbol)
145129 { _name = showRdrName (unLoc feqn_tycon) <> " " <> T.unwords
146130 (map pprText feqn_pats)
@@ -183,35 +167,12 @@ documentSymbolForDecl (L l (ForD x)) = Just
183167
184168documentSymbolForDecl _ = Nothing
185169
186−-- | Wrap the Document imports into a hierarchical outline for
187−-- a better overview of symbols in scope.
188−-- If there are no imports, then no hierarchy will be created.
189−documentSymbolForImportSummary :: [DocumentSymbol] -> Maybe DocumentSymbol
190−documentSymbolForImportSummary [] = Nothing
191−documentSymbolForImportSummary importSymbols =
192− let
193− -- safe because if we have no ranges then we don't take this branch
194− mergeRanges xs = Range (minimum $ map _start xs) (maximum $ map _end xs)
195− importRange = mergeRanges $ map (_range :: DocumentSymbol -> Range) importSymbols
196− in
197− Just (defDocumentSymbol empty :: DocumentSymbol)
198− { _name = "imports"
199− , _kind = SkModule
200− , _children = Just (List importSymbols)
201− , _range = importRange
202− , _selectionRange = importRange
203− }
204−
205170documentSymbolForImport :: Located (ImportDecl GhcPs) -> Maybe DocumentSymbol
206171documentSymbolForImport (L l ImportDecl { ideclName, ideclQualified }) = Just
207172 (defDocumentSymbol l :: DocumentSymbol)
208173 { _name = "import " <> pprText ideclName
209174 , _kind = SkModule
210−#if MIN_GHC_API_VERSION(8,10,0)
211− , _detail = case ideclQualified of { NotQualified -> Nothing; _ -> Just "qualified" }
212−#else
213175 , _detail = if ideclQualified then Just "qualified" else Nothing
214−#endif
215176 }
216177#if MIN_GHC_API_VERSION(8,6,0)
217178documentSymbolForImport (L _ XImportDecl {}) = Nothing
src/Development/IDE/LSP/Server.hs+10−11
@@ -16,32 +16,31 @@ import qualified Language.Haskell.LSP.Core as LSP
1616import qualified Language.Haskell.LSP.Messages as LSP
1717import Development.IDE.Core.Service
1818
19−data WithMessage c = WithMessage
19+
20+data WithMessage = WithMessage
2021 {withResponse :: forall m req resp . (Show m, Show req) =>
2122 (ResponseMessage resp -> LSP.FromServerMessage) -> -- how to wrap a response
22− (LSP.LspFuncs c -> IdeState -> req -> IO (Either ResponseError resp)) -> -- actual work
23+ (LSP.LspFuncs () -> IdeState -> req -> IO resp) -> -- actual work
2324 Maybe (LSP.Handler (RequestMessage m req resp))
2425 ,withNotification :: forall m req . (Show m, Show req) =>
2526 Maybe (LSP.Handler (NotificationMessage m req)) -> -- old notification handler
26− (LSP.LspFuncs c -> IdeState -> req -> IO ()) -> -- actual work
27+ (LSP.LspFuncs () -> IdeState -> req -> IO ()) -> -- actual work
2728 Maybe (LSP.Handler (NotificationMessage m req))
28− ,withResponseAndRequest :: forall m rm req resp newReqParams newReqBody .
29+ ,withResponseAndRequest :: forall m rm req resp newReqParams newReqBody.
2930 (Show m, Show rm, Show req, Show newReqParams, Show newReqBody) =>
3031 (ResponseMessage resp -> LSP.FromServerMessage) -> -- how to wrap a response
3132 (RequestMessage rm newReqParams newReqBody -> LSP.FromServerMessage) -> -- how to wrap the additional req
32− (LSP.LspFuncs c -> IdeState -> req -> IO (Either ResponseError resp, Maybe (rm, newReqParams))) -> -- actual work
33+ (LSP.LspFuncs () -> IdeState -> req -> IO (resp, Maybe (rm, newReqParams))) -> -- actual work
3334 Maybe (LSP.Handler (RequestMessage m req resp))
34− , withInitialize :: (LSP.LspFuncs c -> IdeState -> InitializeParams -> IO ())
35− -> Maybe (LSP.Handler InitializeRequest)
3635 }
3736
38−newtype PartialHandlers c = PartialHandlers (WithMessage c -> LSP.Handlers -> IO LSP.Handlers)
37+newtype PartialHandlers = PartialHandlers (WithMessage -> LSP.Handlers -> IO LSP.Handlers)
3938
40−instance Default (PartialHandlers c) where
39+instance Default PartialHandlers where
4140 def = PartialHandlers $ \_ x -> pure x
4241
43−instance Semigroup (PartialHandlers c) where
42+instance Semigroup PartialHandlers where
4443 PartialHandlers a <> PartialHandlers b = PartialHandlers $ \w x -> a w x >>= b w
4544
46−instance Monoid (PartialHandlers c) where
45+instance Monoid PartialHandlers where
4746 mempty = def
src/Development/IDE/Plugin.hsdeleted−60
@@ -1,60 +0,0 @@
1−
2−module Development.IDE.Plugin(Plugin(..), codeActionPlugin, codeActionPluginWithRules,makeLspCommandId,getPid) where
3−
4−import Data.Default
5−import qualified Data.Text as T
6−import Development.Shake
7−import Development.IDE.LSP.Server
8−
9−import Language.Haskell.LSP.Types
10−import Development.IDE.Compat
11−import Development.IDE.Core.Rules
12−import qualified Language.Haskell.LSP.Core as LSP
13−import Language.Haskell.LSP.Messages
14−
15−
16−data Plugin c = Plugin
17− {pluginRules :: Rules ()
18− ,pluginHandler :: PartialHandlers c
19− }
20−
21−instance Default (Plugin c) where
22− def = Plugin mempty def
23−
24−instance Semigroup (Plugin c) where
25− Plugin x1 y1 <> Plugin x2 y2 = Plugin (x1<>x2) (y1<>y2)
26−
27−instance Monoid (Plugin c) where
28− mempty = def
29−
30−
31−codeActionPlugin :: (LSP.LspFuncs c -> IdeState -> TextDocumentIdentifier -> Range -> CodeActionContext -> IO (Either ResponseError [CAResult])) -> Plugin c
32−codeActionPlugin = codeActionPluginWithRules mempty
33−
34−codeActionPluginWithRules :: Rules () -> (LSP.LspFuncs c -> IdeState -> TextDocumentIdentifier -> Range -> CodeActionContext -> IO (Either ResponseError [CAResult])) -> Plugin c
35−codeActionPluginWithRules rr f = Plugin rr $ PartialHandlers $ \WithMessage{..} x -> return x{
36− LSP.codeActionHandler = withResponse RspCodeAction g
37− }
38− where
39− g lsp state (CodeActionParams a b c _) = fmap List <$> f lsp state a b c
40−
41−-- | Prefix to uniquely identify commands sent to the client. This
42−-- has two parts
43−--
44−-- - A representation of the process id to make sure that a client has
45−-- unique commands if it is running multiple servers, since some
46−-- clients have a global command table and get confused otherwise.
47−--
48−-- - A string to identify ghcide, to ease integration into
49−-- haskell-language-server, which routes commands to plugins based
50−-- on that.
51−makeLspCommandId :: T.Text -> IO T.Text
52−makeLspCommandId command = do
53− pid <- getPid
54− return $ pid <> ":ghcide:" <> command
55−
56−-- | Get the operating system process id for the running server
57−-- instance. This should be the same for the lifetime of the instance,
58−-- and different from that of any other currently running instance.
59−getPid :: IO T.Text
60−getPid = T.pack . show <$> getProcessID
src/Development/IDE/Plugin/CodeAction/PositionIndexed.hsdeleted−112
@@ -1,112 +0,0 @@
1−-- | Position indexed streams of characters
2−module Development.IDE.Plugin.CodeAction.PositionIndexed
3− ( PositionIndexed
4− , PositionIndexedString
5− , indexedByPosition
6− , indexedByPositionStartingFrom
7− , extendAllToIncludeCommaIfPossible
8− , mergeRanges
9− )
10−where
11−
12−import Data.Char
13−import Data.List
14−import Language.Haskell.LSP.Types
15−
16−type PositionIndexed a = [(Position, a)]
17−
18−type PositionIndexedString = PositionIndexed Char
19−
20−-- | Add position indexing to a String.
21−--
22−-- > indexedByPositionStartingFrom (0,0) "hey\n ho" ≡
23−-- > [ ((0,0),'h')
24−-- > , ((0,1),'e')
25−-- > , ((0,2),'y')
26−-- > , ((0,3),'\n')
27−-- > , ((1,0),' ')
28−-- > , ((1,1),'h')
29−-- > , ((1,2),'o')
30−-- > ]
31−indexedByPositionStartingFrom :: Position -> String -> PositionIndexedString
32−indexedByPositionStartingFrom initialPos = unfoldr f . (initialPos, ) where
33− f (_, []) = Nothing
34− f (p@(Position l _), '\n' : rest) =
35− Just ((p, '\n'), (Position (l + 1) 0, rest))
36− f (p@(Position l c), x : rest) = Just ((p, x), (Position l (c + 1), rest))
37−
38−-- | Add position indexing to a String.
39−--
40−-- > indexedByPosition = indexedByPositionStartingFrom (Position 0 0)
41−indexedByPosition :: String -> PositionIndexedString
42−indexedByPosition = indexedByPositionStartingFrom (Position 0 0)
43−
44−-- | Returns a tuple (before, contents, after) if the range is present.
45−-- The range is present only if both its start and end positions are present
46−unconsRange
47− :: Range
48− -> PositionIndexed a
49− -> Maybe (PositionIndexed a, PositionIndexed a, PositionIndexed a)
50−unconsRange Range {..} indexedString
51− | (before, rest@(_ : _)) <- span ((/= _start) . fst) indexedString
52− , (mid, after@(_ : _)) <- span ((/= _end) . fst) rest
53− = Just (before, mid, after)
54− | otherwise
55− = Nothing
56−
57−-- | Strips out all the positions included in the range.
58−-- Returns 'Nothing' if the start or end of the range are not included in the input.
59−stripRange :: Range -> PositionIndexed a -> Maybe (PositionIndexed a)
60−stripRange r s = case unconsRange r s of
61− Just (b, _, a) -> Just (b ++ a)
62− Nothing -> Nothing
63−
64−-- | Returns the smallest possible set of disjoint ranges that is equivalent to the input.
65−-- Assumes input ranges are sorted on the start positions.
66−mergeRanges :: [Range] -> [Range]
67−mergeRanges (r : r' : rest)
68− |
69− -- r' is contained in r
70− _end r > _end r' = mergeRanges (r : rest)
71− |
72− -- r and r' are overlapping
73− _end r > _start r' = mergeRanges (r { _end = _end r' } : rest)
74−
75− | otherwise = r : mergeRanges (r' : rest)
76−mergeRanges other = other
77−
78−-- | Returns a sorted list of ranges with extended selections including preceding or trailing commas
79−--
80−-- @
81−-- a, |b|, c ===> a|, b|, c
82−-- a, b, |c| ===> a, b|, c|
83−-- a, |b|, |c| ===> a|, b||, c|
84−-- @
85−extendAllToIncludeCommaIfPossible :: PositionIndexedString -> [Range] -> [Range]
86−extendAllToIncludeCommaIfPossible indexedString =
87− mergeRanges . go indexedString . sortOn _start
88− where
89− go _ [] = []
90− go input (r : rr)
91− | r' : _ <- extendToIncludeCommaIfPossible input r
92− , Just input' <- stripRange r' input
93− = r' : go input' rr
94− | otherwise
95− = go input rr
96−
97−extendToIncludeCommaIfPossible :: PositionIndexedString -> Range -> [Range]
98−extendToIncludeCommaIfPossible indexedString range
99− | Just (before, _, after) <- unconsRange range indexedString
100− , after' <- dropWhile (isSpace . snd) after
101− , before' <- dropWhile (isSpace . snd) (reverse before)
102− =
103− -- a, |b|, c ===> a|, b|, c
104− [ range { _start = start' } | (start', ',') : _ <- [before'] ]
105− ++
106− -- a, |b|, c ===> a, |b, |c
107− [ range { _end = end' }
108− | (_, ',') : rest <- [after']
109− , let (end', _) : _ = dropWhile (isSpace . snd) rest
110− ]
111− | otherwise
112− = [range]
src/Development/IDE/Plugin/CodeAction/RuleTypes.hsdeleted−61
@@ -1,61 +0,0 @@
1−{-# LANGUAGE TypeFamilies #-}
2−module Development.IDE.Plugin.CodeAction.RuleTypes
3− (PackageExports(..), PackageExportsMap
4− ,IdentInfo(..)
5− ,mkIdentInfos
6− ) where
7−
8−import Avail (AvailInfo(..))
9−import Data.Hashable (Hashable)
10−import Control.DeepSeq (NFData)
11−import Data.Binary (Binary)
12−import Data.Text (pack, Text)
13−import Development.IDE.GHC.Util
14−import Development.Shake (RuleResult)
15−import Data.HashMap.Strict (HashMap)
16−import Data.Typeable (Typeable)
17−import GHC.Generics (Generic)
18−import Name
19−import FieldLabel (flSelector)
20−
21−type Identifier = Text
22−type ModuleName = Text
23−
24−data IdentInfo = IdentInfo
25− { name :: !Identifier
26− , rendered :: Text
27− , parent :: !(Maybe Text)
28− , isDatacon :: !Bool
29− }
30− deriving (Eq, Generic, Show)
31−
32−instance NFData IdentInfo
33−
34−mkIdentInfos :: AvailInfo -> [IdentInfo]
35−mkIdentInfos (Avail n) =
36− [IdentInfo (pack (prettyPrint n)) (pack (printName n)) Nothing (isDataConName n)]
37−mkIdentInfos (AvailTC parent (n:nn) flds)
38− -- Following the GHC convention that parent == n if parent is exported
39− | n == parent
40− = [ IdentInfo (pack (prettyPrint n)) (pack (printName n)) (Just $! parentP) True
41− | n <- nn ++ map flSelector flds
42− ] ++
43− [ IdentInfo (pack (prettyPrint n)) (pack (printName n)) Nothing False]
44− where
45− parentP = pack $ prettyPrint parent
46−
47−mkIdentInfos (AvailTC _ nn flds)
48− = [ IdentInfo (pack (prettyPrint n)) (pack (printName n)) Nothing True
49− | n <- nn ++ map flSelector flds
50− ]
51−
52−-- Rule type for caching Package Exports
53−type instance RuleResult PackageExports = PackageExportsMap
54−type PackageExportsMap = HashMap Identifier [(IdentInfo,ModuleName)]
55−
56−newtype PackageExports = PackageExports HscEnvEq
57− deriving (Eq, Show, Typeable, Generic)
58−
59−instance Hashable PackageExports
60−instance NFData PackageExports
61−instance Binary PackageExports
src/Development/IDE/Plugin/CodeAction/Rules.hsdeleted−61
@@ -1,61 +0,0 @@
1−module Development.IDE.Plugin.CodeAction.Rules
2− ( rulePackageExports
3− )
4−where
5−
6−import Data.HashMap.Strict ( fromListWith )
7−import Data.Text ( Text
8− , pack
9− )
10−import Data.Traversable ( forM )
11−import Development.IDE.Core.Rules
12−import Development.IDE.GHC.Util
13−import Development.IDE.Plugin.CodeAction.RuleTypes
14−import Development.Shake
15−import GHC ( DynFlags(pkgState) )
16−import HscTypes ( IfaceExport
17− , hsc_dflags
18− , mi_exports
19− )
20−import LoadIface
21−import Maybes
22−import Module ( Module(..)
23− , ModuleName
24− , moduleNameString
25− )
26−import Packages ( explicitPackages
27− , exposedModules
28− , packageConfigId
29− )
30−import TcRnMonad ( WhereFrom(ImportByUser)
31− , initIfaceLoad
32− )
33−
34−rulePackageExports :: Rules ()
35−rulePackageExports = defineNoFile $ \(PackageExports session) -> do
36− let env = hscEnv session
37− pkgst = pkgState (hsc_dflags env)
38− depends = explicitPackages pkgst
39− targets =
40− [ (pkg, mn)
41− | d <- depends
42− , Just pkg <- [lookupPackageConfig d env]
43− , (mn, _) <- exposedModules pkg
44− ]
45−
46− results <- forM targets $ \(pkg, mn) -> do
47− modIface <- liftIO $ initIfaceLoad env $ loadInterface
48− ""
49− (Module (packageConfigId pkg) mn)
50− (ImportByUser False)
51− case modIface of
52− Failed _err -> return mempty
53− Succeeded mi -> do
54− let avails = mi_exports mi
55− return $ concatMap (unpackAvail mn) avails
56− return $ fromListWith (++) $ concat results
57−
58−unpackAvail :: ModuleName -> IfaceExport -> [(Text, [(IdentInfo, Text)])]
59−unpackAvail mod =
60− map (\id@IdentInfo {..} -> (name, [(id, pack $ moduleNameString mod)]))
61− . mkIdentInfos
src/Development/IDE/Plugin/Completions.hsdeleted−103
@@ -1,103 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE TypeFamilies #-}
3−#include "ghc-api-version.h"
4−
5−module Development.IDE.Plugin.Completions(plugin) where
6−
7−import Control.Applicative
8−import Language.Haskell.LSP.Messages
9−import Language.Haskell.LSP.Types
10−import qualified Language.Haskell.LSP.Core as LSP
11−import qualified Language.Haskell.LSP.VFS as VFS
12−import Language.Haskell.LSP.Types.Capabilities
13−import Development.Shake.Classes
14−import Development.Shake
15−import GHC.Generics
16−
17−import Development.IDE.Plugin
18−import Development.IDE.Core.Service
19−import Development.IDE.Plugin.Completions.Logic
20−import Development.IDE.Types.Location
21−import Development.IDE.Core.PositionMapping
22−import Development.IDE.Core.RuleTypes
23−import Development.IDE.Core.Shake
24−import Development.IDE.GHC.Util
25−import Development.IDE.LSP.Server
26−
27−#if !MIN_GHC_API_VERSION(8,6,0) || defined(GHC_LIB)
28−import Data.Maybe
29−import Development.IDE.Import.DependencyInformation
30−#endif
31−
32−plugin :: Plugin c
33−plugin = Plugin produceCompletions setHandlersCompletion
34−
35−produceCompletions :: Rules ()
36−produceCompletions =
37− define $ \ProduceCompletions file -> do
38−
39−-- When possible, rely on the haddocks embedded in our interface files
40−-- This creates problems on ghc-lib, see comment on 'getDocumentationTryGhc'
41−#if MIN_GHC_API_VERSION(8,6,0) && !defined(GHC_LIB)
42− let parsedDeps = []
43−#else
44− deps <- maybe (TransitiveDependencies [] [] []) fst <$> useWithStale GetDependencies file
45− parsedDeps <- mapMaybe (fmap fst) <$> usesWithStale GetParsedModule (transitiveModuleDeps deps)
46−#endif
47− tm <- fmap fst <$> useWithStale TypeCheck file
48− packageState <- fmap (hscEnv . fst) <$> useWithStale GhcSession file
49− case (tm, packageState) of
50− (Just tm', Just packageState') -> do
51− cdata <- liftIO $ cacheDataProducer packageState'
52− (tmrModule tm') parsedDeps
53− return ([], Just cdata)
54− _ -> return ([], Nothing)
55−
56−
57−-- | Produce completions info for a file
58−type instance RuleResult ProduceCompletions = CachedCompletions
59−
60−data ProduceCompletions = ProduceCompletions
61− deriving (Eq, Show, Typeable, Generic)
62−instance Hashable ProduceCompletions
63−instance NFData ProduceCompletions
64−instance Binary ProduceCompletions
65−
66−
67−-- | Generate code actions.
68−getCompletionsLSP
69− :: LSP.LspFuncs c
70− -> IdeState
71− -> CompletionParams
72− -> IO (Either ResponseError CompletionResponseResult)
73−getCompletionsLSP lsp ide
74− CompletionParams{_textDocument=TextDocumentIdentifier uri
75− ,_position=position
76− ,_context=completionContext} = do
77− contents <- LSP.getVirtualFileFunc lsp $ toNormalizedUri uri
78− fmap Right $ case (contents, uriToFilePath' uri) of
79− (Just cnts, Just path) -> do
80− let npath = toNormalizedFilePath' path
81− (ideOpts, compls) <- runAction ide $ do
82− opts <- getIdeOptions
83− compls <- useWithStale ProduceCompletions npath
84− pm <- useWithStale GetParsedModule npath
85− pure (opts, liftA2 (,) compls pm)
86− case compls of
87− Just ((cci', _), (pm, mapping)) -> do
88− let !position' = fromCurrentPosition mapping position
89− pfix <- maybe (return Nothing) (flip VFS.getCompletionPrefix cnts) position'
90− case (pfix, completionContext) of
91− (Just (VFS.PosPrefixInfo _ "" _ _), Just CompletionContext { _triggerCharacter = Just "."})
92− -> return (Completions $ List [])
93− (Just pfix', _) -> do
94− let fakeClientCapabilities = ClientCapabilities Nothing Nothing Nothing Nothing
95− Completions . List <$> getCompletions ideOpts cci' pm pfix' fakeClientCapabilities (WithSnippets True)
96− _ -> return (Completions $ List [])
97− _ -> return (Completions $ List [])
98− _ -> return (Completions $ List [])
99−
100−setHandlersCompletion :: PartialHandlers c
101−setHandlersCompletion = PartialHandlers $ \WithMessage{..} x -> return x{
102− LSP.completionHandler = withResponse RspCompletion getCompletionsLSP
103− }
src/Development/IDE/Spans/AtPoint.hs+18−17
@@ -13,12 +13,15 @@ import Development.IDE.GHC.Orphans()
1313import Development.IDE.Types.Location
1414
1515-- DAML compiler and infrastructure
16+import Development.Shake
17+import Development.IDE.GHC.Util
1618import Development.IDE.GHC.Compat
1719import Development.IDE.Types.Options
1820import Development.IDE.Spans.Type as SpanInfo
1921import Development.IDE.Spans.Common (spanDocToMarkdown)
2022
2123-- GHC API imports
24+import Avail
2225import DynFlags
2326import FastString
2427import Name
@@ -37,13 +40,14 @@ import qualified Data.Text as T
3740-- | Locate the definition of the name at a given position.
3841gotoDefinition
3942 :: MonadIO m
40− => (Module -> m (Maybe (HieFile, FilePath)))
43+ => (FilePath -> m (Maybe HieFile))
4144 -> IdeOptions
45+ -> HscEnv
4246 -> [SpanInfo]
4347 -> Position
4448 -> m (Maybe Location)
45−gotoDefinition getHieFile ideOpts srcSpans pos =
46− listToMaybe <$> locationsAtPoint getHieFile ideOpts pos srcSpans
49+gotoDefinition getHieFile ideOpts pkgState srcSpans pos =
50+ listToMaybe <$> locationsAtPoint getHieFile ideOpts pkgState pos srcSpans
4751
4852-- | Synopsis for the name at a given position.
4953atPoint
@@ -83,7 +87,7 @@ atPoint IdeOptions{..} (SpansInfo srcSpans cntsSpans) pos = do
8387 constraintsOverFVs = filter (\cnt -> not (tyCoVarsOfType cnt `disjointVarSet` thisFVs)) cnts
8488 constraintsT = T.intercalate ", " (map showName constraintsOverFVs)
8589
86− typeAnnotation = case constraintsOverFVs of
90+ typeAnnotation = case constraintsOverFVs of
8791 [] -> colon <> showName typ
8892 [_] -> colon <> constraintsT <> "\n=> " <> showName typ
8993 _ -> colon <> "(" <> constraintsT <> ")\n=> " <> showName typ
@@ -115,15 +119,8 @@ atPoint IdeOptions{..} (SpansInfo srcSpans cntsSpans) pos = do
115119 Just name -> any (`isInfixOf` getOccString name) ["==", "showsPrec"]
116120 Nothing -> False
117121
118−locationsAtPoint
119− :: forall m
120− . MonadIO m
121− => (Module -> m (Maybe (HieFile, FilePath)))
122− -> IdeOptions
123− -> Position
124− -> [SpanInfo]
125− -> m [Location]
126−locationsAtPoint getHieFile _ideOptions pos =
122+locationsAtPoint :: forall m . MonadIO m => (FilePath -> m (Maybe HieFile)) -> IdeOptions -> HscEnv -> Position -> [SpanInfo] -> m [Location]
123+locationsAtPoint getHieFile IdeOptions{..} pkgState pos =
127124 fmap (map srcSpanToLocation) . mapMaybeM (getSpan . spaninfoSource) . spansAtPoint pos
128125 where getSpan :: SpanSource -> m (Maybe SrcSpan)
129126 getSpan NoSource = pure Nothing
@@ -133,16 +130,20 @@ locationsAtPoint getHieFile _ideOptions pos =
133130 sp@(RealSrcSpan _) -> pure $ Just sp
134131 sp@(UnhelpfulSpan _) -> runMaybeT $ do
135132 guard (sp /= wiredInSrcSpan)
136− -- This case usually arises when the definition is in an external package (DAML only).
133+ -- This case usually arises when the definition is in an external package.
137134 -- In this case the interface files contain garbage source spans
138135 -- so we instead read the .hie files to get useful source spans.
139136 mod <- MaybeT $ return $ nameModule_maybe name
140− (hieFile, srcPath) <- MaybeT $ getHieFile mod
141− avail <- MaybeT $ pure $ find (eqName name . snd) $ hieExportNames hieFile
137+ let unitId = moduleUnitId mod
138+ pkgConfig <- MaybeT $ pure $ lookupPackageConfig unitId pkgState
139+ hiePath <- MaybeT $ liftIO $ optLocateHieFile optPkgLocationOpts pkgConfig mod
140+ hieFile <- MaybeT $ getHieFile hiePath
141+ avail <- MaybeT $ pure $ listToMaybe (filterAvails (eqName name) $ hie_exports hieFile)
142+ srcPath <- MaybeT $ liftIO $ optLocateSrcFile optPkgLocationOpts pkgConfig mod
142143 -- The location will point to the source file used during compilation.
143144 -- This file might no longer exists and even if it does the path will be relative
144145 -- to the compilation directory which we don’t know.
145− let span = setFileName srcPath $ fst avail
146+ let span = setFileName srcPath $ nameSrcSpan $ availName avail
146147 pure span
147148 -- We ignore uniques and source spans and only compare the name and the module.
148149 eqName :: Name -> Name -> Bool
src/Development/IDE/Spans/Calculate.hs+27−65
@@ -20,7 +20,6 @@ import DataCon
2020import Desugar
2121import GHC
2222import GhcMonad
23−import HscTypes
2423import FastString (mkFastString)
2524import OccName
2625import Development.IDE.Types.Location
@@ -50,36 +49,28 @@ import Development.IDE.Spans.Documentation
5049-- | Get source span info, used for e.g. AtPoint and Goto Definition.
5150getSrcSpanInfos
5251 :: HscEnv
53− -> [(Located ModuleName, Maybe NormalizedFilePath)] -- ^ Dependencies in topological order
52+ -> [(Located ModuleName, Maybe NormalizedFilePath)]
5453 -> TcModuleResult
55− -> [ParsedModule] -- ^ Dependencies parsed, optional
56− -> [ModIface] -- ^ Dependencies module interfaces, required
54+ -> [TcModuleResult]
5755 -> IO SpansInfo
58−getSrcSpanInfos env imports tc parsedDeps deps =
59− evalGhcEnv env $
60− getSpanInfo imports (tmrModule tc) parsedDeps deps
56+getSrcSpanInfos env imports tc tms =
57+ runGhcEnv env $
58+ getSpanInfo imports (tmrModule tc) (map tmrModule tms)
6159
6260-- | Get ALL source spans in the module.
6361getSpanInfo :: GhcMonad m
6462 => [(Located ModuleName, Maybe NormalizedFilePath)] -- ^ imports
6563 -> TypecheckedModule
66− -> [ParsedModule]
67− -> [ModIface]
64+ -> [TypecheckedModule]
6865 -> m SpansInfo
69−getSpanInfo mods tcm@TypecheckedModule{..} parsedDeps deps =
70− do let tcs = tm_typechecked_source
66+getSpanInfo mods tcm tcms =
67+ do let tcs = tm_typechecked_source tcm
7168 bs = listifyAllSpans tcs :: [LHsBind GhcTc]
7269 es = listifyAllSpans tcs :: [LHsExpr GhcTc]
7370 ps = listifyAllSpans' tcs :: [Pat GhcTc]
74− ts = listifyAllSpans tm_renamed_source :: [LHsType GhcRn]
75− allModules = tm_parsed_module : parsedDeps
76− funBinds = funBindMap tm_parsed_module
77−
78− -- Load all modules in HPT to make their interface documentation available
79− mapM_ (`loadDepModule` Nothing) (reverse deps)
80− forM_ (modInfoIface tm_checked_module_info) $ \modIface ->
81− modifySession (loadModuleHome $ HomeModInfo modIface (snd tm_internals_) Nothing)
82−
71+ ts = listifyAllSpans $ tm_renamed_source tcm :: [LHsType GhcRn]
72+ allModules = tcm:tcms
73+ funBinds = funBindMap $ tm_parsed_module tcm
8374 bts <- mapM (getTypeLHsBind allModules funBinds) bs -- binds
8475 ets <- mapM (getTypeLHsExpr allModules) es -- expressions
8576 pts <- mapM (getTypeLPat allModules) ps -- patterns
@@ -126,19 +117,19 @@ ieLNames _ = []
126117
127118-- | Get the name and type of a binding.
128119getTypeLHsBind :: (GhcMonad m)
129− => [ParsedModule]
120+ => [TypecheckedModule]
130121 -> OccEnv (HsBind GhcPs)
131122 -> LHsBind GhcTc
132123 -> m [(SpanSource, SrcSpan, Maybe Type, SpanDoc)]
133−getTypeLHsBind deps funBinds (L _spn FunBind{fun_id = pid})
124+getTypeLHsBind tms funBinds (L _spn FunBind{fun_id = pid})
134125 | Just FunBind {fun_matches = MG{mg_alts=L _ matches}} <- lookupOccEnv funBinds (occName $ unLoc pid) = do
135126 let name = getName (unLoc pid)
136− docs <- getDocumentationTryGhc deps name
127+ docs <- getDocumentationTryGhc tms name
137128 return [(Named name, getLoc mc_fun, Just (varType (unLoc pid)), docs) | match <- matches, FunRhs{mc_fun = mc_fun} <- [m_ctxt $ unLoc match] ]
138129-- In theory this shouldn’t ever fail but if it does, we can at least show the first clause.
139−getTypeLHsBind deps _ (L _spn FunBind{fun_id = pid,fun_matches = MG{}}) = do
130+getTypeLHsBind tms _ (L _spn FunBind{fun_id = pid,fun_matches = MG{}}) = do
140131 let name = getName (unLoc pid)
141− docs <- getDocumentationTryGhc deps name
132+ docs <- getDocumentationTryGhc tms name
142133 return [(Named name, getLoc pid, Just (varType (unLoc pid)), docs)]
143134getTypeLHsBind _ _ _ = return []
144135
@@ -151,23 +142,24 @@ getConstraintsLHsBind _ = []
151142
152143-- | Get the name and type of an expression.
153144getTypeLHsExpr :: (GhcMonad m)
154− => [ParsedModule]
145+ => [TypecheckedModule]
155146 -> LHsExpr GhcTc
156147 -> m (Maybe (SpanSource, SrcSpan, Maybe Type, SpanDoc))
157−getTypeLHsExpr deps e = do
148+getTypeLHsExpr tms e = do
158149 hs_env <- getSession
159150 (_, mbe) <- liftIO (deSugarExpr hs_env e)
160151 case mbe of
161152 Just expr -> do
162153 let ss = getSpanSource (unLoc e)
163154 docs <- case ss of
164− Named n -> getDocumentationTryGhc deps n
155+ Named n -> getDocumentationTryGhc tms n
165156 _ -> return emptySpanDoc
166157 return $ Just (ss, getLoc e, Just (CoreUtils.exprType expr), docs)
167158 Nothing -> return Nothing
168159 where
169160 getSpanSource :: HsExpr GhcTc -> SpanSource
170− getSpanSource xpr | isLit xpr = Lit (showGhc xpr)
161+ getSpanSource (HsLit U lit) = Lit (showGhc lit)
162+ getSpanSource (HsOverLit U lit) = Lit (showGhc lit)
171163 getSpanSource (HsVar U (L _ i)) = Named (getName i)
172164 getSpanSource (HsConLikeOut U (RealDataCon dc)) = Named (dataConName dc)
173165 getSpanSource RecordCon {rcon_con_name} = Named (getName rcon_con_name)
@@ -175,45 +167,15 @@ getTypeLHsExpr deps e = do
175167 getSpanSource (HsPar U xpr) = getSpanSource (unLoc xpr)
176168 getSpanSource _ = NoSource
177169
178− isLit :: HsExpr GhcTc -> Bool
179− isLit (HsLit U _) = True
180− isLit (HsOverLit U _) = True
181− isLit (ExplicitTuple U args _) = all (isTupLit . unLoc) args
182−#if MIN_GHC_API_VERSION(8,6,0)
183− isLit (ExplicitSum U _ _ xpr) = isLitChild (unLoc xpr)
184− isLit (ExplicitList U _ xprs) = all (isLitChild . unLoc) xprs
185−#else
186− isLit (ExplicitSum _ _ xpr _) = isLitChild (unLoc xpr)
187− isLit (ExplicitList _ _ xprs) = all (isLitChild . unLoc) xprs
188−#endif
189− isLit _ = False
190−
191− isTupLit (Present U xpr) = isLitChild (unLoc xpr)
192− isTupLit _ = False
193−
194− -- We need special treatment for children so things like [(1)] are still treated
195− -- as a list literal while not treating (1) as a literal.
196− isLitChild (HsWrap U _ xpr) = isLitChild xpr
197− isLitChild (HsPar U xpr) = isLitChild (unLoc xpr)
198−#if MIN_GHC_API_VERSION(8,8,0)
199− isLitChild (ExprWithTySig U xpr _) = isLitChild (unLoc xpr)
200−#elif MIN_GHC_API_VERSION(8,6,0)
201− isLitChild (ExprWithTySig U xpr) = isLitChild (unLoc xpr)
202−#else
203− isLitChild (ExprWithTySigOut xpr _) = isLitChild (unLoc xpr)
204− isLitChild (ExprWithTySig xpr _) = isLitChild (unLoc xpr)
205−#endif
206− isLitChild e = isLit e
207−
208170-- | Get the name and type of a pattern.
209171getTypeLPat :: (GhcMonad m)
210− => [ParsedModule]
172+ => [TypecheckedModule]
211173 -> Pat GhcTc
212174 -> m (Maybe (SpanSource, SrcSpan, Maybe Type, SpanDoc))
213−getTypeLPat deps pat = do
175+getTypeLPat tms pat = do
214176 let (src, spn) = getSpanSource pat
215177 docs <- case src of
216− Named n -> getDocumentationTryGhc deps n
178+ Named n -> getDocumentationTryGhc tms n
217179 _ -> return emptySpanDoc
218180 return $ Just (src, spn, Just (hsPatType pat), docs)
219181 where
@@ -225,12 +187,12 @@ getTypeLPat deps pat = do
225187
226188getLHsType
227189 :: GhcMonad m
228− => [ParsedModule]
190+ => [TypecheckedModule]
229191 -> LHsType GhcRn
230192 -> m [(SpanSource, SrcSpan, Maybe Type, SpanDoc)]
231−getLHsType deps (L spn (HsTyVar U _ v)) = do
193+getLHsType tms (L spn (HsTyVar U _ v)) = do
232194 let n = unLoc v
233− docs <- getDocumentationTryGhc deps n
195+ docs <- getDocumentationTryGhc tms n
234196#ifdef GHC_LIB
235197 let ty = Right Nothing
236198#else
src/Development/IDE/Spans/Common.hs+13−15
@@ -12,13 +12,11 @@ module Development.IDE.Spans.Common (
1212, SpanDoc(..)
1313, emptySpanDoc
1414, spanDocToMarkdown
15−, spanDocToMarkdownForTest
1615) where
1716
1817import Data.Data
1918import qualified Data.Generics
2019import qualified Data.Text as T
21−import Data.List.Extra
2220
2321import GHC
2422import Outputable
@@ -29,8 +27,11 @@ import DataCon
2927import Var
3028#endif
3129
30+#if MIN_GHC_API_VERSION(8,6,0)
31+import Data.Char (isSpace)
3232import qualified Documentation.Haddock.Parser as H
3333import qualified Documentation.Haddock.Types as H
34+#endif
3435
3536showGhc :: Outputable a => a -> String
3637showGhc = showPpr unsafeGlobalDynFlags
@@ -80,10 +81,7 @@ spanDocToMarkdown (SpanDocString _)
8081#endif
8182spanDocToMarkdown (SpanDocText txt) = txt
8283
83−spanDocToMarkdownForTest :: String -> String
84−spanDocToMarkdownForTest
85− = haddockToMarkdown . H.toRegular . H._doc . H.parseParas Nothing
86−
84+#if MIN_GHC_API_VERSION(8,6,0)
8785-- Simple (and a bit hacky) conversion from Haddock markup to Markdown
8886haddockToMarkdown
8987 :: H.DocH String String -> String
@@ -91,7 +89,7 @@ haddockToMarkdown
9189haddockToMarkdown H.DocEmpty
9290 = ""
9391haddockToMarkdown (H.DocAppend d1 d2)
94− = haddockToMarkdown d1 ++ " " ++ haddockToMarkdown d2
92+ = haddockToMarkdown d1 Prelude.<> haddockToMarkdown d2
9593haddockToMarkdown (H.DocString s)
9694 = s
9795haddockToMarkdown (H.DocParagraph p)
@@ -123,8 +121,13 @@ haddockToMarkdown (H.DocExamples es)
123121 = ">>> " ++ expr ++ "\n" ++ unlines result
124122haddockToMarkdown (H.DocHyperlink (H.Hyperlink url Nothing))
125123 = "<" ++ url ++ ">"
124+#if MIN_VERSION_haddock_library(1,8,0)
126125haddockToMarkdown (H.DocHyperlink (H.Hyperlink url (Just label)))
127126 = "[" ++ haddockToMarkdown label ++ "](" ++ url ++ ")"
127+#else
128+haddockToMarkdown (H.DocHyperlink (H.Hyperlink url (Just label)))
129+ = "[" ++ label ++ "](" ++ url ++ ")"
130+#endif
128131haddockToMarkdown (H.DocPic (H.Picture url Nothing))
129132 = "![](" ++ url ++ ")"
130133haddockToMarkdown (H.DocPic (H.Picture url (Just label)))
@@ -135,9 +138,9 @@ haddockToMarkdown (H.DocHeader (H.Header level title))
135138 = replicate level '#' ++ " " ++ haddockToMarkdown title
136139
137140haddockToMarkdown (H.DocUnorderedList things)
138− = '\n' : (unlines $ map (("+ " ++) . trimStart . splitForList . haddockToMarkdown) things)
141+ = '\n' : (unlines $ map (\thing -> "+ " ++ dropWhile isSpace (haddockToMarkdown thing)) things)
139142haddockToMarkdown (H.DocOrderedList things)
140− = '\n' : (unlines $ map (("1. " ++) . trimStart . splitForList . haddockToMarkdown) things)
143+ = '\n' : (unlines $ map (\thing -> "1. " ++ dropWhile isSpace (haddockToMarkdown thing)) things)
141144haddockToMarkdown (H.DocDefList things)
142145 = '\n' : (unlines $ map (\(term, defn) -> "+ **" ++ haddockToMarkdown term ++ "**: " ++ haddockToMarkdown defn) things)
143146
@@ -154,9 +157,4 @@ haddockToMarkdown (H.DocTable _t)
154157-- things I don't really know how to handle
155158haddockToMarkdown (H.DocProperty _)
156159 = "" -- don't really know what to do
157−
158−splitForList :: String -> String
159−splitForList s
160− = case lines s of
161− [] -> ""
162− (first:rest) -> unlines $ first : map ((" " ++) . trimStart) rest
160+#endif
src/Development/IDE/Spans/Documentation.hs+19−21
@@ -14,33 +14,33 @@ import Data.List.Extra
1414import qualified Data.Map as M
1515import Data.Maybe
1616import qualified Data.Text as T
17−import Development.IDE.GHC.Compat
1817import Development.IDE.GHC.Error
1918import Development.IDE.Spans.Common
2019import FastString
20+import GHC
2121import SrcLoc
2222
2323
2424getDocumentationTryGhc
2525 :: GhcMonad m
26− => [ParsedModule]
26+ => [TypecheckedModule]
2727 -> Name
2828 -> m SpanDoc
2929-- getDocs goes through the GHCi codepaths which cause problems on ghc-lib.
3030-- See https://github.com/digital-asset/daml/issues/4152 for more details.
3131#if MIN_GHC_API_VERSION(8,6,0) && !defined(GHC_LIB)
32−getDocumentationTryGhc sources name = do
32+getDocumentationTryGhc tcs name = do
3333 res <- catchSrcErrors "docs" $ getDocs name
3434 case res of
3535 Right (Right (Just docs, _)) -> return $ SpanDocString docs
36− _ -> return $ SpanDocText $ getDocumentation sources name
36+ _ -> return $ SpanDocText $ getDocumentation tcs name
3737#else
38−getDocumentationTryGhc sources name = do
39− return $ SpanDocText $ getDocumentation sources name
38+getDocumentationTryGhc tcs name = do
39+ return $ SpanDocText $ getDocumentation tcs name
4040#endif
4141
4242getDocumentation
43− :: [ParsedModule] -- ^ All of the possible modules it could be defined in.
43+ :: [TypecheckedModule] -- ^ All of the possible modules it could be defined in.
4444 -> Name -- ^ The name you want documentation for.
4545 -> [T.Text]
4646-- This finds any documentation between the name you want
@@ -50,18 +50,16 @@ getDocumentation
5050-- may be edge cases where it is very wrong).
5151-- TODO : Build a version of GHC exactprint to extract this information
5252-- more accurately.
53−getDocumentation sources targetName = fromMaybe [] $ do
53+getDocumentation tcs targetName = fromMaybe [] $ do
5454 -- Find the module the target is defined in.
5555 targetNameSpan <- realSpan $ nameSrcSpan targetName
5656 tc <-
5757 find ((==) (Just $ srcSpanFile targetNameSpan) . annotationFileName)
58− $ reverse sources -- TODO : Is reversing the list here really neccessary?
59−
60− -- Top level names bound by the module
61− let bs = [ n | let L _ HsModule{hsmodDecls} = pm_parsed_source tc
62− , L _ (ValD hsbind) <- hsmodDecls
63− , Just n <- [name_of_bind hsbind]
64− ]
58+ $ reverse tcs -- TODO : Is reversing the list here really neccessary?
59+ -- Names bound by the module (we want to exclude non-"top-level"
60+ -- bindings but unfortunately we get all here).
61+ let bs = mapMaybe name_of_bind
62+ (listifyAllSpans (tm_typechecked_source tc) :: [LHsBind GhcTc])
6563 -- Sort the names' source spans.
6664 let sortedSpans = sortedNameSpans bs
6765 -- Now go ahead and extract the docs.
@@ -83,16 +81,16 @@ getDocumentation sources targetName = fromMaybe [] $ do
8381 where
8482 -- Get the name bound by a binding. We only concern ourselves with
8583 -- @FunBind@ (which covers functions and variables).
86− name_of_bind :: HsBind GhcPs -> Maybe (Located RdrName)
87− name_of_bind FunBind {fun_id} = Just fun_id
84+ name_of_bind :: LHsBind GhcTc -> Maybe Name
85+ name_of_bind (L _ FunBind {fun_id}) = Just (getName (unLoc fun_id))
8886 name_of_bind _ = Nothing
8987 -- Get source spans from names, discard unhelpful spans, remove
9088 -- duplicates and sort.
91− sortedNameSpans :: [Located RdrName] -> [RealSrcSpan]
92− sortedNameSpans ls = nubSort (mapMaybe (realSpan . getLoc) ls)
89+ sortedNameSpans :: [Name] -> [RealSrcSpan]
90+ sortedNameSpans ls = nubSort (mapMaybe (realSpan . nameSrcSpan) ls)
9391 isBetween target before after = before <= target && target <= after
94− ann = snd . pm_annotations
95− annotationFileName :: ParsedModule -> Maybe FastString
92+ ann = snd . pm_annotations . tm_parsed_module
93+ annotationFileName :: TypecheckedModule -> Maybe FastString
9694 annotationFileName = fmap srcSpanFile . listToMaybe . realSpans . ann
9795 realSpans :: M.Map SrcSpan [Located a] -> [RealSrcSpan]
9896 realSpans =
src/Development/IDE/Types/Diagnostics.hs+5−19
@@ -6,12 +6,10 @@ module Development.IDE.Types.Diagnostics (
66 LSP.Diagnostic(..),
77 ShowDiagnostic(..),
88 FileDiagnostic,
9− IdeResult,
109 LSP.DiagnosticSeverity(..),
1110 DiagnosticStore,
1211 List(..),
1312 ideErrorText,
14− ideErrorWithSource,
1513 showDiagnostics,
1614 showDiagnosticsColored,
1715 ) where
@@ -20,7 +18,7 @@ import Control.DeepSeq
2018import Data.Maybe as Maybe
2119import qualified Data.Text as T
2220import Data.Text.Prettyprint.Doc
23−import Language.Haskell.LSP.Types as LSP (DiagnosticSource,
21+import Language.Haskell.LSP.Types as LSP (
2422 DiagnosticSeverity(..)
2523 , Diagnostic(..)
2624 , List(..)
@@ -32,27 +30,15 @@ import Data.Text.Prettyprint.Doc.Render.Terminal (Color(..), color)
3230
3331import Development.IDE.Types.Location
3432
35−-- A rule on a file should only return diagnostics for that given file. It should
36−-- not propagate diagnostic errors through multiple phases.
37−type IdeResult v = ([FileDiagnostic], Maybe v)
3833
3934ideErrorText :: NormalizedFilePath -> T.Text -> FileDiagnostic
40−ideErrorText = ideErrorWithSource (Just "compiler") (Just DsError)
41−
42−ideErrorWithSource
43− :: Maybe DiagnosticSource
44− -> Maybe DiagnosticSeverity
45− -> a
46− -> T.Text
47− -> (a, ShowDiagnostic, Diagnostic)
48−ideErrorWithSource source sev fp msg = (fp, ShowDiag, LSP.Diagnostic {
35+ideErrorText fp msg = (fp, ShowDiag, LSP.Diagnostic {
4936 _range = noRange,
50− _severity = sev,
37+ _severity = Just LSP.DsError,
5138 _code = Nothing,
52− _source = source,
39+ _source = Just "compiler",
5340 _message = msg,
54− _relatedInformation = Nothing,
55− _tags = Nothing
41+ _relatedInformation = Nothing
5642 })
5743
5844-- | Defines whether a particular diagnostic should be reported
src/Development/IDE/Types/Location.hs+79−24
@@ -10,16 +10,15 @@ module Development.IDE.Types.Location
1010 , Position(..)
1111 , showPosition
1212 , Range(..)
13− , LSP.Uri(..)
14− , LSP.NormalizedUri
13+ , Uri(..)
14+ , NormalizedUri
1515 , LSP.toNormalizedUri
1616 , LSP.fromNormalizedUri
17− , LSP.NormalizedFilePath
17+ , NormalizedFilePath
1818 , fromUri
19− , emptyFilePath
20− , emptyPathUri
21− , toNormalizedFilePath'
22− , LSP.fromNormalizedFilePath
19+ , toNormalizedFilePath
20+ , fromNormalizedFilePath
21+ , filePathToUri
2322 , filePathToUri'
2423 , uriToFilePath'
2524 , readSrcSpan
@@ -27,42 +26,98 @@ module Development.IDE.Types.Location
2726
2827import Control.Applicative
2928import Language.Haskell.LSP.Types (Location(..), Range(..), Position(..))
29+import Control.DeepSeq
3030import Control.Monad
31−import Data.Hashable (Hashable(hash))
31+import Data.Binary
32+import Data.Maybe as Maybe
33+import Data.Hashable
3234import Data.String
35+import qualified Data.Text as T
3336import FastString
37+import Network.URI
38+import System.FilePath
39+import qualified System.FilePath.Posix as FPP
40+import qualified System.FilePath.Windows as FPW
41+import System.Info.Extra
3442import qualified Language.Haskell.LSP.Types as LSP
43+import Language.Haskell.LSP.Types as LSP (
44+ filePathToUri
45+ , NormalizedUri(..)
46+ , Uri(..)
47+ , toNormalizedUri
48+ , fromNormalizedUri
49+ )
3550import SrcLoc as GHC
3651import Text.ParserCombinators.ReadP as ReadP
37−import Data.Maybe (fromMaybe)
3852
39−toNormalizedFilePath' :: FilePath -> LSP.NormalizedFilePath
53+
54+-- | Newtype wrapper around FilePath that always has normalized slashes.
55+newtype NormalizedFilePath = NormalizedFilePath FilePath
56+ deriving (Eq, Ord, Show, Hashable, NFData, Binary)
57+
58+instance IsString NormalizedFilePath where
59+ fromString = toNormalizedFilePath
60+
61+toNormalizedFilePath :: FilePath -> NormalizedFilePath
4062-- We want to keep empty paths instead of normalising them to "."
41−toNormalizedFilePath' "" = emptyFilePath
42−toNormalizedFilePath' fp = LSP.toNormalizedFilePath fp
63+toNormalizedFilePath "" = NormalizedFilePath ""
64+toNormalizedFilePath fp = NormalizedFilePath $ normalise fp
4365
44−emptyFilePath :: LSP.NormalizedFilePath
45−emptyFilePath = LSP.NormalizedFilePath emptyPathUri ""
66+fromNormalizedFilePath :: NormalizedFilePath -> FilePath
67+fromNormalizedFilePath (NormalizedFilePath fp) = fp
4668
4769-- | We use an empty string as a filepath when we don’t have a file.
4870-- However, haskell-lsp doesn’t support that in uriToFilePath and given
4971-- that it is not a valid filepath it does not make sense to upstream a fix.
5072-- So we have our own wrapper here that supports empty filepaths.
51−uriToFilePath' :: LSP.Uri -> Maybe FilePath
73+uriToFilePath' :: Uri -> Maybe FilePath
5274uriToFilePath' uri
53− | uri == LSP.fromNormalizedUri emptyPathUri = Just ""
75+ | uri == fromNormalizedUri emptyPathUri = Just ""
5476 | otherwise = LSP.uriToFilePath uri
5577
56−emptyPathUri :: LSP.NormalizedUri
57−emptyPathUri =
58− let s = "file://"
59− in LSP.NormalizedUri (hash s) s
78+emptyPathUri :: NormalizedUri
79+emptyPathUri = filePathToUri' ""
6080
61−filePathToUri' :: LSP.NormalizedFilePath -> LSP.NormalizedUri
62−filePathToUri' = LSP.normalizedFilePathToUri
81+filePathToUri' :: NormalizedFilePath -> NormalizedUri
82+filePathToUri' (NormalizedFilePath fp) = toNormalizedUri $ Uri $ T.pack $ LSP.fileScheme <> "//" <> platformAdjustToUriPath fp
83+ where
84+ -- The definitions below are variants of the corresponding functions in Language.Haskell.LSP.Types.Uri that assume that
85+ -- the filepath has already been normalised. This is necessary since normalising the filepath has a nontrivial cost.
86+
87+ toNormalizedUri :: Uri -> NormalizedUri
88+ toNormalizedUri (Uri t) =
89+ NormalizedUri $ T.pack $ escapeURIString isUnescapedInURI $ unEscapeString $ T.unpack t
90+
91+ platformAdjustToUriPath :: FilePath -> String
92+ platformAdjustToUriPath srcPath
93+ | isWindows = '/' : escapedPath
94+ | otherwise = escapedPath
95+ where
96+ (splitDirectories, splitDrive)
97+ | isWindows =
98+ (FPW.splitDirectories, FPW.splitDrive)
99+ | otherwise =
100+ (FPP.splitDirectories, FPP.splitDrive)
101+ escapedPath =
102+ case splitDrive srcPath of
103+ (drv, rest) ->
104+ convertDrive drv `FPP.joinDrive`
105+ FPP.joinPath (map (escapeURIString unescaped) $ splitDirectories rest)
106+ -- splitDirectories does not remove the path separator after the drive so
107+ -- we do a final replacement of \ to /
108+ convertDrive drv
109+ | isWindows && FPW.hasTrailingPathSeparator drv =
110+ FPP.addTrailingPathSeparator (init drv)
111+ | otherwise = drv
112+ unescaped c
113+ | isWindows = isUnreserved c || c `elem` [':', '\\', '/']
114+ | otherwise = isUnreserved c || c == '/'
115+
116+
117+
118+fromUri :: LSP.NormalizedUri -> NormalizedFilePath
119+fromUri = toNormalizedFilePath . fromMaybe noFilePath . uriToFilePath' . fromNormalizedUri
63120
64−fromUri :: LSP.NormalizedUri -> LSP.NormalizedFilePath
65−fromUri = fromMaybe (toNormalizedFilePath' noFilePath) . LSP.uriToNormalizedFilePath
66121
67122noFilePath :: FilePath
68123noFilePath = "<unknown>"
src/Development/IDE/Types/Options.hs+8−35
@@ -12,25 +12,25 @@ module Development.IDE.Types.Options
1212 , clientSupportsProgress
1313 , IdePkgLocationOptions(..)
1414 , defaultIdeOptions
15− , IdeResult
1615 ) where
1716
17+import Data.Maybe
1818import Development.Shake
1919import Development.IDE.GHC.Util
2020import GHC hiding (parseModule, typecheckModule)
2121import GhcPlugins as GHC hiding (fst3, (<>))
2222import qualified Language.Haskell.LSP.Types.Capabilities as LSP
23−import qualified Data.Text as T
24−import Development.IDE.Types.Diagnostics
2523
2624data IdeOptions = IdeOptions
2725 { optPreprocessor :: GHC.ParsedSource -> IdePreprocessedSource
2826 -- ^ Preprocessor to run over all parsed source trees, generating a list of warnings
2927 -- and a list of errors, along with a new parse tree.
30− , optGhcSession :: Action (FilePath -> Action (IdeResult HscEnvEq))
28+ , optGhcSession :: IO (FilePath -> Action HscEnvEq)
3129 -- ^ Setup a GHC session for a given file, e.g. @Foo.hs@.
32− -- For the same 'ComponentOptions' from hie-bios, the resulting function will be applied once per file.
30+ -- The 'IO' will be called once, then the resulting function will be applied once per file.
3331 -- It is desirable that many files get the same 'HscEnvEq', so that more IDE features work.
32+ -- You should not use 'newCacheIO' to get that caching, because of
33+ -- https://github.com/ndmitchell/shake/issues/725.
3434 , optPkgLocationOpts :: IdePkgLocationOptions
3535 -- ^ How to locate source and @.hie@ files given a module name.
3636 , optExtensions :: [String]
@@ -43,17 +43,12 @@ data IdeOptions = IdeOptions
4343 -- meaning we keep everything in memory but the daml CLI compiler uses this for incremental builds.
4444 , optShakeProfiling :: Maybe FilePath
4545 -- ^ Set to 'Just' to create a directory of profiling reports.
46− , optTesting :: Bool
47− -- ^ Whether to enable additional lsp messages used by the test suite for checking invariants
4846 , optReportProgress :: IdeReportProgress
4947 -- ^ Whether to report progress during long operations.
5048 , optLanguageSyntax :: String
5149 -- ^ the ```language to use
5250 , optNewColonConvention :: Bool
5351 -- ^ whether to use new colon convention
54− , optKeywords :: [T.Text]
55− -- ^ keywords used for completions. These are customizable
56− -- since DAML has a different set of keywords than Haskell.
5752 , optDefer :: IdeDefer
5853 -- ^ Whether to defer type errors, typed holes and out of scope
5954 -- variables. Deferral allows the IDE to continue to provide
@@ -75,10 +70,10 @@ newtype IdeReportProgress = IdeReportProgress Bool
7570newtype IdeDefer = IdeDefer Bool
7671
7772clientSupportsProgress :: LSP.ClientCapabilities -> IdeReportProgress
78−clientSupportsProgress caps = IdeReportProgress $ Just True ==
79− (LSP._workDoneProgress =<< LSP._window (caps :: LSP.ClientCapabilities))
73+clientSupportsProgress caps = IdeReportProgress $ fromMaybe False $
74+ LSP._workDoneProgress =<< LSP._window (caps :: LSP.ClientCapabilities)
8075
81−defaultIdeOptions :: Action (FilePath -> Action (IdeResult HscEnvEq)) -> IdeOptions
76+defaultIdeOptions :: IO (FilePath -> Action HscEnvEq) -> IdeOptions
8277defaultIdeOptions session = IdeOptions
8378 {optPreprocessor = IdePreprocessedSource [] []
8479 ,optGhcSession = session
@@ -90,9 +85,7 @@ defaultIdeOptions session = IdeOptions
9085 ,optReportProgress = IdeReportProgress False
9186 ,optLanguageSyntax = "haskell"
9287 ,optNewColonConvention = False
93− ,optKeywords = haskellKeywords
9488 ,optDefer = IdeDefer True
95− ,optTesting = False
9689 }
9790
9891
@@ -110,23 +103,3 @@ data IdePkgLocationOptions = IdePkgLocationOptions
110103defaultIdePkgLocationOptions :: IdePkgLocationOptions
111104defaultIdePkgLocationOptions = IdePkgLocationOptions f f
112105 where f _ _ = return Nothing
113−
114−-- | From https://wiki.haskell.org/Keywords
115−haskellKeywords :: [T.Text]
116−haskellKeywords =
117− [ "as"
118− , "case", "of"
119− , "class", "instance", "type"
120− , "data", "family", "newtype"
121− , "default"
122− , "deriving"
123− , "do", "mdo", "proc", "rec"
124− , "forall"
125− , "foreign"
126− , "hiding"
127− , "if", "then", "else"
128− , "import", "qualified", "hiding"
129− , "infix", "infixl", "infixr"
130− , "let", "in", "where"
131− , "module"
132− ]
stack-ghc-lib.yaml+4−7
@@ -2,18 +2,15 @@ resolver: nightly-2019-09-16
22packages:
33- .
44extra-deps:
5−- haskell-lsp-0.22.0.0
6−- haskell-lsp-types-0.22.0.0
7−- lsp-test-0.10.3.0
8−- hie-bios-0.5.0
5+- haskell-lsp-0.19.0.0
6+- haskell-lsp-types-0.19.0.0
7+- lsp-test-0.10.0.0
8+- hie-bios-0.3.2
99- ghc-lib-parser-8.8.1
1010- ghc-lib-8.8.1
1111- fuzzy-0.1.0.0
12−- shake-0.18.5
1312- regex-base-0.94.0.0
1413- regex-tdfa-1.3.1.0
15−- haddock-library-1.8.0
16−- ghc-check-0.3.0.1
1714nix:
1815 packages: [zlib]
1916flags:
stack.yaml+4−8
@@ -2,18 +2,14 @@ resolver: nightly-2019-09-21
22packages:
33- .
44extra-deps:
5−- haskell-lsp-0.22.0.0
6−- haskell-lsp-types-0.22.0.0
7−- lsp-test-0.11.0.1
8−- hie-bios-0.5.0
5+- haskell-lsp-0.19.0.0
6+- haskell-lsp-types-0.19.0.0
7+- lsp-test-0.10.0.0
8+- hie-bios-0.3.2
99- fuzzy-0.1.0.0
1010- regex-pcre-builtin-0.95.1.1.8.43
1111- regex-base-0.94.0.0
1212- regex-tdfa-1.3.1.0
13−- shake-0.18.5
1413- parser-combinators-1.2.1
15−- haddock-library-1.8.0
16−- tasty-rerun-1.1.17
17−- ghc-check-0.3.0.1
1814nix:
1915 packages: [zlib]
stack810.yamldeleted−18
@@ -1,18 +0,0 @@
1−resolver: nightly-2020-02-13
2−compiler: ghc-8.10.1
3−allow-newer: true
4−packages:
5−- .
6−extra-deps:
7−- haskell-lsp-0.22.0.0
8−- haskell-lsp-types-0.22.0.0
9−- lsp-test-0.11.0.1
10−- ghc-check-0.3.0.1
11−- hie-bios-0.5.0
12−
13−# for ghc-8.10
14−- Cabal-3.2.0.0
15−- lens-4.19.1
16−
17−nix:
18− packages: [zlib]
stack84.yaml+6−19
@@ -3,31 +3,18 @@ packages:
33- .
44
55extra-deps:
6−- aeson-1.4.6.0
7−- base-orphans-0.8.2
8−- haskell-lsp-0.22.0.0
9−- haskell-lsp-types-0.22.0.0
10−- lsp-test-0.11.0.1
6+- haskell-lsp-0.19.0.0
7+- haskell-lsp-types-0.19.0.0
8+- lsp-test-0.10.0.0
119- rope-utf16-splay-0.3.1.0
10+- shake-0.18.3
1211- filepattern-0.1.1
1312- js-dgtable-0.5.2
14−- hie-bios-0.5.0
13+- hie-bios-0.3.2
1514- fuzzy-0.1.0.0
16−- shake-0.18.5
17−- time-compat-1.9.2.2
1815- regex-base-0.94.0.0
1916- regex-tdfa-1.3.1.0
2017- parser-combinators-1.2.1
21−- haddock-library-1.8.0
22−- unordered-containers-0.2.10.0
23−- file-embed-0.0.11.2
24−- heaps-0.3.6.1
25−- ghc-check-0.3.0.1
26−# For tasty-retun
27−- ansi-terminal-0.10.3
28−- ansi-wl-pprint-0.6.9
29−- tasty-1.2.3
30−- tasty-rerun-1.1.17
31−
3218nix:
3319 packages: [zlib]
20+allow-newer: true
stack88.yaml+3−6
@@ -1,11 +1,8 @@
1−resolver: nightly-2020-02-13
1+resolver: nightly-2020-01-21
22packages:
33- .
44extra-deps:
5−- haskell-lsp-0.22.0.0
6−- haskell-lsp-types-0.22.0.0
7−- lsp-test-0.11.0.1
8−- ghc-check-0.3.0.1
9−- hie-bios-0.5.0
5+- fuzzy-0.1.0.0
6+allow-newer: true
107nix:
118 packages: [zlib]
test/data/hover/GotoHover.hs → test/data/GotoHover.hsrenamed−7
@@ -1,10 +1,6 @@
1−{-# LANGUAGE OverloadedStrings #-}
21{- HLINT ignore -}
32module Testing ( module Testing ) where
43import Data.Text (Text, pack)
5−import Foo (Bar, foo)
6−
7−
84data TypeConstructor = DataConstructor
95 { fff :: Text
106 , ggg :: Int }
@@ -52,6 +48,3 @@ outer = undefined where
5248
5349 inner :: Char
5450 inner = undefined
55−
56−imported :: Bar
57−imported = foo
test/data/hover/Bar.hsdeleted−3
@@ -1,3 +0,0 @@
1−module Bar (Bar(..)) where
2−
3−data Bar = Bar
test/data/hover/Foo.hsdeleted−5
@@ -1,5 +0,0 @@
1−module Foo (Bar, foo) where
2−
3−import Bar
4−
5−foo = Bar
test/data/multi/a/A.hsdeleted−3
@@ -1,3 +0,0 @@
1−module A(foo) where
2−
3−foo = ()
test/data/multi/a/a.cabaldeleted−9
@@ -1,9 +0,0 @@
1−name: a
2−version: 1.0.0
3−build-type: Simple
4−cabal-version: >= 1.2
5−
6−library
7− build-depends: base
8− exposed-modules: A
9− hs-source-dirs: .
test/data/multi/b/B.hsdeleted−3
@@ -1,3 +0,0 @@
1−module B(module B) where
2−import A
3−qux = foo
test/data/multi/b/b.cabaldeleted−9
@@ -1,9 +0,0 @@
1−name: b
2−version: 1.0.0
3−build-type: Simple
4−cabal-version: >= 1.2
5−
6−library
7− build-depends: base, a
8− exposed-modules: B
9− hs-source-dirs: .
test/data/multi/cabal.projectdeleted−1
@@ -1 +0,0 @@
1−packages: a b
test/data/multi/hie.yamldeleted−6
@@ -1,6 +0,0 @@
1−cradle:
2− cabal:
3− - path: "./a"
4− component: "lib:a"
5− - path: "./b"
6− component: "lib:b"
test/exe/Main.hs+283−1120
@@ -1,7 +1,6 @@
11-- Copyright (c) 2019 The DAML Authors. All rights reserved.
22-- SPDX-License-Identifier: Apache-2.0
33
4−{-# LANGUAGE AllowAmbiguousTypes #-}
54{-# LANGUAGE DuplicateRecordFields #-}
65{-# LANGUAGE PatternSynonyms #-}
76{-# LANGUAGE CPP #-}
@@ -10,74 +9,51 @@
109module Main (main) where
1110
1211import Control.Applicative.Combinators
13−import Control.Exception (bracket, catch)
14−import qualified Control.Lens as Lens
12+import Control.Exception (catch)
1513import Control.Monad
1614import Control.Monad.IO.Class (liftIO)
17−import Data.Aeson (FromJSON, Value)
15+import Data.Char (toLower)
1816import Data.Foldable
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)
17+import Data.List
2318import Development.IDE.GHC.Util
2419import qualified Data.Text as T
25−import Data.Typeable
26−import Development.IDE.Spans.Common
2720import Development.IDE.Test
2821import Development.IDE.Test.Runfiles
2922import Development.IDE.Types.Location
30−import Development.Shake (getDirectoryFilesIO)
31−import Language.Haskell.LSP.Test
32−import Language.Haskell.LSP.Messages
23+import qualified Language.Haskell.LSP.Test as LSPTest
24+import Language.Haskell.LSP.Test hiding (openDoc')
3325import Language.Haskell.LSP.Types
3426import 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)
37−import Network.URI
38−import System.Environment.Blank (getEnv, setEnv, unsetEnv)
27+import System.Environment.Blank (setEnv)
3928import System.FilePath
4029import System.IO.Extra
4130import System.Directory
42−import Test.QuickCheck
43−import Test.QuickCheck.Instances ()
4431import Test.Tasty
45−import Test.Tasty.ExpectedFailure
46−import Test.Tasty.Ingredients.Rerun
4732import Test.Tasty.HUnit
48−import Test.Tasty.QuickCheck
33+import Test.Tasty.ExpectedFailure
4934import Data.Maybe
5035
5136main :: IO ()
52−main = do
53− -- We mess with env vars so run single-threaded.
54− setEnv "TASTY_NUM_THREADS" "1" True
55− defaultMainWithRerun $ testGroup "HIE"
56− [ testSession "open close" $ do
57− doc <- createDoc "Testing.hs" "haskell" ""
58− void (skipManyTill anyMessage message :: Session WorkDoneProgressCreateRequest)
59− void (skipManyTill anyMessage message :: Session WorkDoneProgressBeginNotification)
60− closeDoc doc
61− void (skipManyTill anyMessage message :: Session WorkDoneProgressEndNotification)
62− , initializeResponseTests
63− , completionTests
64− , cppTests
65− , diagnosticTests
66− , codeActionTests
67− , codeLensesTests
68− , outlineTests
69− , findDefinitionAndHoverTests
70− , pluginTests
71− , preprocessorTests
72− , thTests
73− , safeTests
74− , unitTests
75− , haddockTests
76− , positionMappingTests
77− , watchedFilesTests
78− , cradleTests
79− , dependentFileTest
80− ]
37+main = defaultMain $ testGroup "HIE"
38+ [ testSession "open close" $ do
39+ doc <- openDoc' "Testing.hs" "haskell" ""
40+ void (message :: Session WorkDoneProgressCreateRequest)
41+ void (message :: Session WorkDoneProgressBeginNotification)
42+ closeDoc doc
43+ void (message :: Session WorkDoneProgressEndNotification)
44+ , initializeResponseTests
45+ , completionTests
46+ , cppTests
47+ , diagnosticTests
48+ , codeActionTests
49+ , codeLensesTests
50+ , outlineTests
51+ , findDefinitionAndHoverTests
52+ , pluginTests
53+ , preprocessorTests
54+ , thTests
55+ , unitTests
56+ ]
8157
8258initializeResponseTests :: TestTree
8359initializeResponseTests = withResource acquire release tests where
@@ -112,8 +88,8 @@ initializeResponseTests = withResource acquire release tests where
11288 , chk "NO doc link" _documentLinkProvider Nothing
11389 , chk "NO color" _colorProvider (Just $ ColorOptionsStatic False)
11490 , chk "NO folding range" _foldingRangeProvider (Just $ FoldingRangeOptionsStatic False)
115− , che " execute command" _executeCommandProvider (Just $ ExecuteCommandOptions $ List ["typesignature.add"])
116− , chk " workspace" _workspace (Just $ WorkspaceOptions (Just WorkspaceFolderOptions{_supported = Just True, _changeNotifications = Just ( WorkspaceFolderChangeNotificationsBool True )}))
91+ , chk " execute command" _executeCommandProvider (Just $ ExecuteCommandOptions $ List ["typesignature.add"])
92+ , chk "NO workspace" _workspace nothingWorkspace
11793 , chk "NO experimental" _experimental Nothing
11894 ] where
11995
@@ -124,21 +100,14 @@ initializeResponseTests = withResource acquire release tests where
124100 , _willSaveWaitUntil = Nothing
125101 , _save = Just (SaveOptions {_includeText = Nothing})}))
126102
103+ nothingWorkspace = Just (WorkspaceOptions {_workspaceFolders = Nothing})
104+
127105 chk :: (Eq a, Show a) => TestName -> (InitializeResponseCapabilitiesInner -> a) -> a -> TestTree
128106 chk title getActual expected =
129107 testCase title $ getInitializeResponse >>= \ir -> expected @=? (getActual . innerCaps) ir
130108
131− che :: TestName -> (InitializeResponseCapabilitiesInner -> Maybe ExecuteCommandOptions) -> Maybe ExecuteCommandOptions -> TestTree
132− che title getActual _expected = testCase title doTest
133− where
134− doTest = do
135− ir <- getInitializeResponse
136− let Just ExecuteCommandOptions {_commands = List [command]} = getActual $ innerCaps ir
137− True @=? T.isSuffixOf "typesignature.add" command
138−
139−
140109 innerCaps :: InitializeResponse -> InitializeResponseCapabilitiesInner
141− innerCaps (ResponseMessage _ _ (Right (InitializeResponseCapabilities c))) = c
110+ innerCaps (ResponseMessage _ _ (Just (InitializeResponseCapabilities c)) _) = c
142111 innerCaps _ = error "this test only expects inner capabilities"
143112
144113 acquire :: IO InitializeResponse
@@ -152,7 +121,7 @@ diagnosticTests :: TestTree
152121diagnosticTests = testGroup "diagnostics"
153122 [ testSessionWait "fix syntax error" $ do
154123 let content = T.unlines [ "module Testing wher" ]
155− doc <- createDoc "Testing.hs" "haskell" content
124+ doc <- openDoc' "Testing.hs" "haskell" content
156125 expectDiagnostics [("Testing.hs", [(DsError, (0, 15), "parse error")])]
157126 let change = TextDocumentContentChangeEvent
158127 { _range = Just (Range (Position 0 15) (Position 0 19))
@@ -163,9 +132,9 @@ diagnosticTests = testGroup "diagnostics"
163132 expectDiagnostics [("Testing.hs", [])]
164133 , testSessionWait "introduce syntax error" $ do
165134 let content = T.unlines [ "module Testing where" ]
166− doc <- createDoc "Testing.hs" "haskell" content
167− void $ skipManyTill anyMessage (message :: Session WorkDoneProgressCreateRequest)
168− void $ skipManyTill anyMessage (message :: Session WorkDoneProgressBeginNotification)
135+ doc <- openDoc' "Testing.hs" "haskell" content
136+ void (message :: Session WorkDoneProgressCreateRequest)
137+ void (message :: Session WorkDoneProgressBeginNotification)
169138 let change = TextDocumentContentChangeEvent
170139 { _range = Just (Range (Position 0 15) (Position 0 18))
171140 , _rangeLength = Nothing
@@ -181,7 +150,7 @@ diagnosticTests = testGroup "diagnostics"
181150 , "bar :: Int -> Int -> Int"
182151 , "bar a b = cd + b"
183152 ]
184− _ <- createDoc "Testing.hs" "haskell" content
153+ _ <- openDoc' "Testing.hs" "haskell" content
185154 expectDiagnostics
186155 [ ( "Testing.hs"
187156 , [ (DsError, (2, 14), "Variable not in scope: ab")
@@ -195,7 +164,7 @@ diagnosticTests = testGroup "diagnostics"
195164 , "foo :: Int -> String -> Int"
196165 , "foo a b = a + b"
197166 ]
198− _ <- createDoc "Testing.hs" "haskell" content
167+ _ <- openDoc' "Testing.hs" "haskell" content
199168 expectDiagnostics
200169 [ ( "Testing.hs"
201170 , [(DsError, (2, 14), "Couldn't match type '[Char]' with 'Int'")]
@@ -207,7 +176,7 @@ diagnosticTests = testGroup "diagnostics"
207176 , "foo :: Int -> String"
208177 , "foo a = _ a"
209178 ]
210− _ <- createDoc "Testing.hs" "haskell" content
179+ _ <- openDoc' "Testing.hs" "haskell" content
211180 expectDiagnostics
212181 [ ( "Testing.hs"
213182 , [(DsError, (2, 8), "Found hole: _ :: Int -> String")]
@@ -229,23 +198,24 @@ diagnosticTests = testGroup "diagnostics"
229198 [ ("A.hs", [(DsError, (2,4), aMessage)])
230199 , ("B.hs", [(DsError, (3,4), bMessage)])]
231200 deferralTest title binding msg = testSessionWait title $ do
232− _ <- createDoc "A.hs" "haskell" $ sourceA binding
233− _ <- createDoc "B.hs" "haskell" sourceB
201+ _ <- openDoc' "A.hs" "haskell" $ sourceA binding
202+ _ <- openDoc' "B.hs" "haskell" sourceB
234203 expectDiagnostics $ expectedDs msg
235204 in
236205 [ deferralTest "type error" "True" "Couldn't match expected type"
237206 , deferralTest "typed hole" "_" "Found hole"
238207 , deferralTest "out of scope var" "unbound" "Variable not in scope"
208+ , deferralTest "message shows error" "True" "A.hs:3:5: error:"
239209 ]
240210
241211 , testSessionWait "remove required module" $ do
242212 let contentA = T.unlines [ "module ModuleA where" ]
243− docA <- createDoc "ModuleA.hs" "haskell" contentA
213+ docA <- openDoc' "ModuleA.hs" "haskell" contentA
244214 let contentB = T.unlines
245215 [ "module ModuleB where"
246216 , "import ModuleA"
247217 ]
248− _ <- createDoc "ModuleB.hs" "haskell" contentB
218+ _ <- openDoc' "ModuleB.hs" "haskell" contentB
249219 let change = TextDocumentContentChangeEvent
250220 { _range = Just (Range (Position 0 0) (Position 0 20))
251221 , _rangeLength = Nothing
@@ -258,21 +228,11 @@ diagnosticTests = testGroup "diagnostics"
258228 [ "module ModuleB where"
259229 , "import ModuleA"
260230 ]
261− _ <- createDoc "ModuleB.hs" "haskell" contentB
231+ _ <- openDoc' "ModuleB.hs" "haskell" contentB
262232 expectDiagnostics [("ModuleB.hs", [(DsError, (1, 7), "Could not find module")])]
263233 let contentA = T.unlines [ "module ModuleA where" ]
264− _ <- createDoc "ModuleA.hs" "haskell" contentA
234+ _ <- openDoc' "ModuleA.hs" "haskell" contentA
265235 expectDiagnostics [("ModuleB.hs", [])]
266− , testSessionWait "add missing module (non workspace)" $ do
267− let contentB = T.unlines
268− [ "module ModuleB where"
269− , "import ModuleA"
270− ]
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", [])]
276236 , testSessionWait "cyclic module dependency" $ do
277237 let contentA = T.unlines
278238 [ "module ModuleA where"
@@ -282,8 +242,8 @@ diagnosticTests = testGroup "diagnostics"
282242 [ "module ModuleB where"
283243 , "import ModuleA"
284244 ]
285− _ <- createDoc "ModuleA.hs" "haskell" contentA
286− _ <- createDoc "ModuleB.hs" "haskell" contentB
245+ _ <- openDoc' "ModuleA.hs" "haskell" contentA
246+ _ <- openDoc' "ModuleB.hs" "haskell" contentB
287247 expectDiagnostics
288248 [ ( "ModuleA.hs"
289249 , [(DsError, (1, 7), "Cyclic module dependency between ModuleA, ModuleB")]
@@ -304,9 +264,9 @@ diagnosticTests = testGroup "diagnostics"
304264 let contentBboot = T.unlines
305265 [ "module ModuleB where"
306266 ]
307− _ <- createDoc "ModuleA.hs" "haskell" contentA
308− _ <- createDoc "ModuleB.hs" "haskell" contentB
309− _ <- createDoc "ModuleB.hs-boot" "haskell" contentBboot
267+ _ <- openDoc' "ModuleA.hs" "haskell" contentA
268+ _ <- openDoc' "ModuleB.hs" "haskell" contentB
269+ _ <- openDoc' "ModuleB.hs-boot" "haskell" contentBboot
310270 expectDiagnostics []
311271 , testSessionWait "correct reference used with hs-boot" $ do
312272 let contentB = T.unlines
@@ -328,10 +288,10 @@ diagnosticTests = testGroup "diagnostics"
328288 -- resolved to the hs-boot file
329289 , "y = x"
330290 ]
331− _ <- createDoc "ModuleB.hs" "haskell" contentB
332− _ <- createDoc "ModuleA.hs" "haskell" contentA
333− _ <- createDoc "ModuleA.hs-boot" "haskell" contentAboot
334− _ <- createDoc "ModuleC.hs" "haskell" contentC
291+ _ <- openDoc' "ModuleB.hs" "haskell" contentB
292+ _ <- openDoc' "ModuleA.hs" "haskell" contentA
293+ _ <- openDoc' "ModuleA.hs-boot" "haskell" contentAboot
294+ _ <- openDoc' "ModuleC.hs" "haskell" contentC
335295 expectDiagnostics []
336296 , testSessionWait "redundant import" $ do
337297 let contentA = T.unlines ["module ModuleA where"]
@@ -340,8 +300,8 @@ diagnosticTests = testGroup "diagnostics"
340300 , "module ModuleB where"
341301 , "import ModuleA"
342302 ]
343− _ <- createDoc "ModuleA.hs" "haskell" contentA
344− _ <- createDoc "ModuleB.hs" "haskell" contentB
303+ _ <- openDoc' "ModuleA.hs" "haskell" contentA
304+ _ <- openDoc' "ModuleB.hs" "haskell" contentB
345305 expectDiagnostics
346306 [ ( "ModuleB.hs"
347307 , [(DsWarning, (2, 0), "The import of 'ModuleA' is redundant")]
@@ -363,8 +323,8 @@ diagnosticTests = testGroup "diagnostics"
363323 , "wrong1 = ThisList.map"
364324 , "wrong2 = BaseList.x"
365325 ]
366− _ <- createDoc "Data/List.hs" "haskell" thisDataListContent
367− _ <- createDoc "Main.hs" "haskell" mainContent
326+ _ <- openDoc' "Data/List.hs" "haskell" thisDataListContent
327+ _ <- openDoc' "Main.hs" "haskell" mainContent
368328 expectDiagnostics
369329 [ ( "Main.hs"
370330 , [(DsError, (6, 9), "Not in scope: \8216ThisList.map\8217")
@@ -379,7 +339,7 @@ diagnosticTests = testGroup "diagnostics"
379339 , "foo :: Ord a => a -> Int"
380340 , "foo a = 1"
381341 ]
382− _ <- createDoc "Foo.hs" "haskell" fooContent
342+ _ <- openDoc' "Foo.hs" "haskell" fooContent
383343 expectDiagnostics
384344 [ ( "Foo.hs"
385345 -- The test is to make sure that warnings contain unqualified names
@@ -404,18 +364,18 @@ diagnosticTests = testGroup "diagnostics"
404364 Just pathB <- pure $ uriToFilePath uriB
405365 uriB <- pure $
406366 let (drive, suffix) = splitDrive pathB
407− in filePathToUri (joinDrive (lower drive) suffix)
367+ in filePathToUri (joinDrive (map toLower drive ) suffix)
408368 liftIO $ createDirectoryIfMissing True (takeDirectory pathB)
409369 liftIO $ writeFileUTF8 pathB $ T.unpack bContent
410370 uriA <- getDocUri "A/A.hs"
411371 Just pathA <- pure $ uriToFilePath uriA
412372 uriA <- pure $
413373 let (drive, suffix) = splitDrive pathA
414− in filePathToUri (joinDrive (lower drive) suffix)
374+ in filePathToUri (joinDrive (map toLower drive ) suffix)
415375 let itemA = TextDocumentItem uriA "haskell" 0 aContent
416376 let a = TextDocumentIdentifier uriA
417377 sendNotification TextDocumentDidOpen (DidOpenTextDocumentParams itemA)
418− diagsNot <- skipManyTill anyMessage diagnostic
378+ diagsNot <- skipManyTill anyMessage message :: Session PublishDiagnosticsNotification
419379 let PublishDiagnosticsParams fileUri diags = _params (diagsNot :: PublishDiagnosticsNotification)
420380 -- Check that if we put a lower-case drive in for A.A
421381 -- the diagnostics for A.B will also be lower-case.
@@ -424,38 +384,6 @@ diagnosticTests = testGroup "diagnostics"
424384 liftIO $ unless ("redundant" `T.isInfixOf` msg) $
425385 assertFailure ("Expected redundant import but got " <> T.unpack msg)
426386 closeDoc a
427− , testSessionWait "haddock parse error" $ do
428− let fooContent = T.unlines
429− [ "module Foo where"
430− , "foo :: Int"
431− , "foo = 1 {-|-}"
432− ]
433− _ <- createDoc "Foo.hs" "haskell" fooContent
434− expectDiagnostics
435− [ ( "Foo.hs"
436− , [(DsError, (2, 8), "Parse error on input")
437− ]
438− )
439− ]
440− , testSessionWait "strip file path" $ do
441− let
442− name = "Testing"
443− content = T.unlines
444− [ "module " <> name <> " where"
445− , "value :: Maybe ()"
446− , "value = [()]"
447− ]
448− _ <- createDoc (T.unpack name <> ".hs") "haskell" content
449− notification <- skipManyTill anyMessage diagnostic
450− let
451− offenders =
452− Lsp.params .
453− Lsp.diagnostics .
454− Lens.folded .
455− Lsp.message .
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
459387 ]
460388
461389codeActionTests :: TestTree
@@ -464,8 +392,6 @@ codeActionTests = testGroup "code actions"
464392 , typeWildCardActionTests
465393 , removeImportTests
466394 , extendImportTests
467− , suggestImportTests
468− , addExtensionTests
469395 , fixConstructorImportTests
470396 , importRenameActionTests
471397 , fillTypedHoleTests
@@ -478,33 +404,6 @@ codeLensesTests = testGroup "code lenses"
478404 [ addSigLensesTests
479405 ]
480406
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
487−
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
494−
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
499−
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
504−
505− -- TODO add a test for didChangeWorkspaceFolder
506− ]
507−
508407renameActionTests :: TestTree
509408renameActionTests = testGroup "rename actions"
510409 [ testSession "change to local variable name" $ do
@@ -513,7 +412,7 @@ renameActionTests = testGroup "rename actions"
513412 , "foo :: Int -> Int"
514413 , "foo argName = argNme"
515414 ]
516− doc <- createDoc "Testing.hs" "haskell" content
415+ doc <- openDoc' "Testing.hs" "haskell" content
517416 _ <- waitForDiagnostics
518417 action <- findCodeAction doc (Range (Position 2 14) (Position 2 20)) "Replace with ‘argName’"
519418 executeCodeAction action
@@ -531,7 +430,7 @@ renameActionTests = testGroup "rename actions"
531430 , "foo :: Maybe a -> [a]"
532431 , "foo = maybToList"
533432 ]
534− doc <- createDoc "Testing.hs" "haskell" content
433+ doc <- openDoc' "Testing.hs" "haskell" content
535434 _ <- waitForDiagnostics
536435 action <- findCodeAction doc (Range (Position 3 6) (Position 3 16)) "Replace with ‘maybeToList’"
537436 executeCodeAction action
@@ -549,7 +448,7 @@ renameActionTests = testGroup "rename actions"
549448 , "foo :: Char -> Char -> Char -> Char"
550449 , "foo argument1 argument2 argument3 = argumentX"
551450 ]
552− doc <- createDoc "Testing.hs" "haskell" content
451+ doc <- openDoc' "Testing.hs" "haskell" content
553452 _ <- waitForDiagnostics
554453 _ <- findCodeActions doc (Range (Position 2 36) (Position 2 45))
555454 ["Replace with ‘argument1’", "Replace with ‘argument2’", "Replace with ‘argument3’"]
@@ -561,7 +460,7 @@ renameActionTests = testGroup "rename actions"
561460 , "monus x y = max 0 (x - y)"
562461 , "foo x y = x `monnus` y"
563462 ]
564− doc <- createDoc "Testing.hs" "haskell" content
463+ doc <- openDoc' "Testing.hs" "haskell" content
565464 _ <- waitForDiagnostics
566465 actionsOrCommands <- getCodeActions doc (Range (Position 3 12) (Position 3 20))
567466 [fixTypo] <- pure [action | CACodeAction action@CodeAction{ _title = actionTitle } <- actionsOrCommands, "monus" `T.isInfixOf` actionTitle ]
@@ -584,7 +483,7 @@ typeWildCardActionTests = testGroup "type wildcard actions"
584483 , "func :: _"
585484 , "func x = x"
586485 ]
587− doc <- createDoc "Testing.hs" "haskell" content
486+ doc <- openDoc' "Testing.hs" "haskell" content
588487 _ <- waitForDiagnostics
589488 actionsOrCommands <- getCodeActions doc (Range (Position 2 1) (Position 2 10))
590489 let [addSignature] = [action | CACodeAction action@CodeAction { _title = actionTitle } <- actionsOrCommands
@@ -604,7 +503,7 @@ typeWildCardActionTests = testGroup "type wildcard actions"
604503 , "func :: _"
605504 , "func x y = x + y"
606505 ]
607− doc <- createDoc "Testing.hs" "haskell" content
506+ doc <- openDoc' "Testing.hs" "haskell" content
608507 _ <- waitForDiagnostics
609508 actionsOrCommands <- getCodeActions doc (Range (Position 2 1) (Position 2 10))
610509 let [addSignature] = [action | CACodeAction action@CodeAction { _title = actionTitle } <- actionsOrCommands
@@ -627,7 +526,7 @@ typeWildCardActionTests = testGroup "type wildcard actions"
627526 , " y = x * 2"
628527 , " in y"
629528 ]
630− doc <- createDoc "Testing.hs" "haskell" content
529+ doc <- openDoc' "Testing.hs" "haskell" content
631530 _ <- waitForDiagnostics
632531 actionsOrCommands <- getCodeActions doc (Range (Position 4 1) (Position 4 10))
633532 let [addSignature] = [action | CACodeAction action@CodeAction { _title = actionTitle } <- actionsOrCommands
@@ -652,7 +551,7 @@ removeImportTests = testGroup "remove import actions"
652551 let contentA = T.unlines
653552 [ "module ModuleA where"
654553 ]
655− _docA <- createDoc "ModuleA.hs" "haskell" contentA
554+ _docA <- openDoc' "ModuleA.hs" "haskell" contentA
656555 let contentB = T.unlines
657556 [ "{-# OPTIONS_GHC -Wunused-imports #-}"
658557 , "module ModuleB where"
@@ -660,7 +559,7 @@ removeImportTests = testGroup "remove import actions"
660559 , "stuffB :: Integer"
661560 , "stuffB = 123"
662561 ]
663− docB <- createDoc "ModuleB.hs" "haskell" contentB
562+ docB <- openDoc' "ModuleB.hs" "haskell" contentB
664563 _ <- waitForDiagnostics
665564 [CACodeAction action@CodeAction { _title = actionTitle }]
666565 <- getCodeActions docB (Range (Position 2 0) (Position 2 5))
@@ -678,7 +577,7 @@ removeImportTests = testGroup "remove import actions"
678577 let contentA = T.unlines
679578 [ "module ModuleA where"
680579 ]
681− _docA <- createDoc "ModuleA.hs" "haskell" contentA
580+ _docA <- openDoc' "ModuleA.hs" "haskell" contentA
682581 let contentB = T.unlines
683582 [ "{-# OPTIONS_GHC -Wunused-imports #-}"
684583 , "module ModuleB where"
@@ -686,7 +585,7 @@ removeImportTests = testGroup "remove import actions"
686585 , "stuffB :: Integer"
687586 , "stuffB = 123"
688587 ]
689− docB <- createDoc "ModuleB.hs" "haskell" contentB
588+ docB <- openDoc' "ModuleB.hs" "haskell" contentB
690589 _ <- waitForDiagnostics
691590 [CACodeAction action@CodeAction { _title = actionTitle }]
692591 <- getCodeActions docB (Range (Position 2 0) (Position 2 5))
@@ -708,14 +607,14 @@ removeImportTests = testGroup "remove import actions"
708607 , "stuffB = 123"
709608 , "stuffC = ()"
710609 ]
711− _docA <- createDoc "ModuleA.hs" "haskell" contentA
610+ _docA <- openDoc' "ModuleA.hs" "haskell" contentA
712611 let contentB = T.unlines
713612 [ "{-# OPTIONS_GHC -Wunused-imports #-}"
714613 , "module ModuleB where"
715614 , "import ModuleA (stuffA, stuffB, stuffC, stuffA)"
716615 , "main = print stuffB"
717616 ]
718− docB <- createDoc "ModuleB.hs" "haskell" contentB
617+ docB <- openDoc' "ModuleB.hs" "haskell" contentB
719618 _ <- waitForDiagnostics
720619 [CACodeAction action@CodeAction { _title = actionTitle }]
721620 <- getCodeActions docB (Range (Position 2 0) (Position 2 5))
@@ -729,26 +628,29 @@ removeImportTests = testGroup "remove import actions"
729628 , "main = print stuffB"
730629 ]
731630 liftIO $ expectedContentAfterAction @=? contentAfterAction
732− , testSession "redundant operator" $ do
631+ , testSession "redundant symbol binding" $ do
733632 let contentA = T.unlines
734633 [ "module ModuleA where"
735634 , "a !! b = a"
736− , "a <?> b = a"
737635 , "stuffB :: Integer"
738636 , "stuffB = 123"
739637 ]
740− _docA <- createDoc "ModuleA.hs" "haskell" contentA
638+ _docA <- openDoc' "ModuleA.hs" "haskell" contentA
741639 let contentB = T.unlines
742640 [ "{-# OPTIONS_GHC -Wunused-imports #-}"
743641 , "module ModuleB where"
744− , "import qualified ModuleA as A ((<?>), stuffB, (!!))"
642+ , "import qualified ModuleA as A ((!!), stuffB, (!!))"
745643 , "main = print A.stuffB"
746644 ]
747− docB <- createDoc "ModuleB.hs" "haskell" contentB
645+ docB <- openDoc' "ModuleB.hs" "haskell" contentB
748646 _ <- waitForDiagnostics
749647 [CACodeAction action@CodeAction { _title = actionTitle }]
750648 <- getCodeActions docB (Range (Position 2 0) (Position 2 5))
751− liftIO $ "Remove !!, <?> from import" @=? actionTitle
649+#if MIN_GHC_API_VERSION(8,6,0)
650+ liftIO $ "Remove !! from import" @=? actionTitle
651+#else
652+ liftIO $ "Remove A.!! from import" @=? actionTitle
653+#endif
752654 executeCodeAction action
753655 contentAfterAction <- documentContents docB
754656 let expectedContentAfterAction = T.unlines
@@ -758,21 +660,21 @@ removeImportTests = testGroup "remove import actions"
758660 , "main = print A.stuffB"
759661 ]
760662 liftIO $ expectedContentAfterAction @=? contentAfterAction
761− , testSession "redundant all import" $ do
663+ , (`xfail` "known broken (#299)") $ testSession "redundant hierarchical import" $ do
762664 let contentA = T.unlines
763665 [ "module ModuleA where"
764666 , "data A = A"
765667 , "stuffB :: Integer"
766668 , "stuffB = 123"
767669 ]
768− _docA <- createDoc "ModuleA.hs" "haskell" contentA
670+ _docA <- openDoc' "ModuleA.hs" "haskell" contentA
769671 let contentB = T.unlines
770672 [ "{-# OPTIONS_GHC -Wunused-imports #-}"
771673 , "module ModuleB where"
772674 , "import ModuleA (A(..), stuffB)"
773675 , "main = print stuffB"
774676 ]
775− docB <- createDoc "ModuleB.hs" "haskell" contentB
677+ docB <- openDoc' "ModuleB.hs" "haskell" contentB
776678 _ <- waitForDiagnostics
777679 [CACodeAction action@CodeAction { _title = actionTitle }]
778680 <- getCodeActions docB (Range (Position 2 0) (Position 2 5))
@@ -786,33 +688,6 @@ removeImportTests = testGroup "remove import actions"
786688 , "main = print stuffB"
787689 ]
788690 liftIO $ expectedContentAfterAction @=? contentAfterAction
789− , testSession "redundant constructor import" $ do
790− let contentA = T.unlines
791− [ "module ModuleA where"
792− , "data D = A | B"
793− , "data E = F"
794− ]
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))"
800− , "main = B"
801− ]
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))"
813− , "main = B"
814− ]
815− liftIO $ expectedContentAfterAction @=? contentAfterAction
816691 ]
817692
818693extendImportTests :: TestTree
@@ -837,26 +712,6 @@ extendImportTests = testGroup "extend import actions"
837712 , "import ModuleA as A (stuffA, stuffB)"
838713 , "main = print (stuffA, stuffB)"
839714 ])
840− , testSession "extend single line import with operator" $ template
841− (T.unlines
842− [ "module ModuleA where"
843− , "(.*) :: Integer -> Integer -> Integer"
844− , "x .* y = x * y"
845− , "stuffB :: Integer"
846− , "stuffB = 123"
847− ])
848− (T.unlines
849− [ "module ModuleB where"
850− , "import ModuleA as A (stuffB)"
851− , "main = print (stuffB .* stuffB)"
852− ])
853− (Range (Position 3 17) (Position 3 18))
854− "Add .* to the import list of ModuleA"
855− (T.unlines
856− [ "module ModuleB where"
857− , "import ModuleA as A ((.*), stuffB)"
858− , "main = print (stuffB .* stuffB)"
859− ])
860715 , testSession "extend single line import with type" $ template
861716 (T.unlines
862717 [ "module ModuleA where"
@@ -940,8 +795,8 @@ extendImportTests = testGroup "extend import actions"
940795 ]
941796 where
942797 template contentA contentB range expectedAction expectedContentB = do
943− _docA <- createDoc "ModuleA.hs" "haskell" contentA
944− docB <- createDoc "ModuleB.hs" "haskell" contentB
798+ _docA <- openDoc' "ModuleA.hs" "haskell" contentA
799+ docB <- openDoc' "ModuleB.hs" "haskell" contentB
945800 _ <- waitForDiagnostics
946801 CACodeAction action@CodeAction { _title = actionTitle } : _
947802 <- sortOn (\(CACodeAction CodeAction{_title=x}) -> x) <$>
@@ -951,117 +806,6 @@ extendImportTests = testGroup "extend import actions"
951806 contentAfterAction <- documentContents docB
952807 liftIO $ expectedContentB @=? contentAfterAction
953808
954−suggestImportTests :: TestTree
955−suggestImportTests = testGroup "suggest import actions"
956− [ testGroup "Dont want suggestion"
957− [ -- extend import
958− test False ["Data.List.NonEmpty ()"] "f = nonEmpty" [] "import Data.List.NonEmpty (nonEmpty)"
959− -- data constructor
960− , test False [] "f = First" [] "import Data.Monoid (First)"
961− -- internal module
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)"
965− ]
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 ((&))"
986− ]
987− ]
988− where
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
999− if wanted
1000− then do
1001− action <- liftIO $ pickActionWithTitle newImp actions
1002− executeCodeAction action
1003− contentAfterAction <- documentContents doc
1004− liftIO $ after @=? contentAfterAction
1005− else
1006− liftIO $ [_title | CACodeAction CodeAction{_title} <- actions, _title == newImp ] @?= []
1007−
1008−
1009−addExtensionTests :: TestTree
1010−addExtensionTests = testGroup "add language extension actions"
1011− [ testSession "add NamedFieldPuns language extension" $ template
1012− (T.unlines
1013− [ "module Module where"
1014− , ""
1015− , "data A = A { getA :: Bool }"
1016− , ""
1017− , "f :: A -> Bool"
1018− , "f A { getA } = getA"
1019− ])
1020− (Range (Position 0 0) (Position 0 0))
1021− "Add NamedFieldPuns extension"
1022− (T.unlines
1023− [ "{-# LANGUAGE NamedFieldPuns #-}"
1024− , "module Module where"
1025− , ""
1026− , "data A = A { getA :: Bool }"
1027− , ""
1028− , "f :: A -> Bool"
1029− , "f A { getA } = getA"
1030− ])
1031− , testSession "add RecordWildCards language extension" $ template
1032− (T.unlines
1033− [ "module Module where"
1034− , ""
1035− , "data A = A { getA :: Bool }"
1036− , ""
1037− , "f :: A -> Bool"
1038− , "f A { .. } = getA"
1039− ])
1040− (Range (Position 0 0) (Position 0 0))
1041− "Add RecordWildCards extension"
1042− (T.unlines
1043− [ "{-# LANGUAGE RecordWildCards #-}"
1044− , "module Module where"
1045− , ""
1046− , "data A = A { getA :: Bool }"
1047− , ""
1048− , "f :: A -> Bool"
1049− , "f A { .. } = getA"
1050− ])
1051− ]
1052− where
1053− template initialContent range expectedAction expectedContents = do
1054− doc <- createDoc "Module.hs" "haskell" initialContent
1055− _ <- waitForDiagnostics
1056− CACodeAction action@CodeAction { _title = actionTitle } : _
1057− <- sortOn (\(CACodeAction CodeAction{_title=x}) -> x) <$>
1058− getCodeActions doc range
1059− liftIO $ expectedAction @=? actionTitle
1060− executeCodeAction action
1061− contentAfterAction <- documentContents doc
1062− liftIO $ expectedContents @=? contentAfterAction
1063−
1064−
1065809insertNewDefinitionTests :: TestTree
1066810insertNewDefinitionTests = testGroup "insert new definition actions"
1067811 [ testSession "insert new function definition" $ do
@@ -1074,7 +818,7 @@ insertNewDefinitionTests = testGroup "insert new definition actions"
1074818 [""
1075819 ,"someOtherCode = ()"
1076820 ]
1077− docB <- createDoc "ModuleB.hs" "haskell" (T.unlines $ txtB ++ txtB')
821+ docB <- openDoc' "ModuleB.hs" "haskell" (T.unlines $ txtB ++ txtB')
1078822 _ <- waitForDiagnostics
1079823 CACodeAction action@CodeAction { _title = actionTitle } : _
1080824 <- sortOn (\(CACodeAction CodeAction{_title=x}) -> x) <$>
@@ -1098,7 +842,7 @@ insertNewDefinitionTests = testGroup "insert new definition actions"
1098842 [""
1099843 ,"someOtherCode = ()"
1100844 ]
1101− docB <- createDoc "ModuleB.hs" "haskell" (T.unlines $ txtB ++ txtB')
845+ docB <- openDoc' "ModuleB.hs" "haskell" (T.unlines $ txtB ++ txtB')
1102846 _ <- waitForDiagnostics
1103847 CACodeAction action@CodeAction { _title = actionTitle } : _
1104848 <- sortOn (\(CACodeAction CodeAction{_title=x}) -> x) <$>
@@ -1137,8 +881,8 @@ fixConstructorImportTests = testGroup "fix import actions"
1137881 ]
1138882 where
1139883 template contentA contentB range expectedAction expectedContentB = do
1140− _docA <- createDoc "ModuleA.hs" "haskell" contentA
1141− docB <- createDoc "ModuleB.hs" "haskell" contentB
884+ _docA <- openDoc' "ModuleA.hs" "haskell" contentA
885+ docB <- openDoc' "ModuleB.hs" "haskell" contentB
1142886 _diags <- waitForDiagnostics
1143887 CACodeAction action@CodeAction { _title = actionTitle } : _
1144888 <- sortOn (\(CACodeAction CodeAction{_title=x}) -> x) <$>
@@ -1157,7 +901,7 @@ importRenameActionTests = testGroup "import rename actions"
1157901 [ "module Testing where"
1158902 , "import Data.Mape"
1159903 ]
1160− doc <- createDoc "Testing.hs" "haskell" content
904+ doc <- openDoc' "Testing.hs" "haskell" content
1161905 _ <- waitForDiagnostics
1162906 actionsOrCommands <- getCodeActions doc (Range (Position 2 8) (Position 2 16))
1163907 let [changeToMap] = [action | CACodeAction action@CodeAction{ _title = actionTitle } <- actionsOrCommands, ("Data." <> modname) `T.isInfixOf` actionTitle ]
@@ -1194,10 +938,10 @@ fillTypedHoleTests = let
1194938 newA newB newC = testSession (T.unpack actionTitle) $ do
1195939 let originalCode = sourceCode oldA oldB oldC
1196940 let expectedCode = sourceCode newA newB newC
1197− doc <- createDoc "Testing.hs" "haskell" originalCode
941+ doc <- openDoc' "Testing.hs" "haskell" originalCode
1198942 _ <- waitForDiagnostics
1199943 actionsOrCommands <- getCodeActions doc (Range (Position 9 0) (Position 9 maxBound))
1200− chosenAction <- liftIO $ pickActionWithTitle actionTitle actionsOrCommands
944+ let chosenAction = pickActionWithTitle actionTitle actionsOrCommands
1201945 executeCodeAction chosenAction
1202946 modifiedCode <- documentContents doc
1203947 liftIO $ expectedCode @=? modifiedCode
@@ -1234,37 +978,36 @@ fillTypedHoleTests = let
1234978
1235979addSigActionTests :: TestTree
1236980addSigActionTests = let
1237− header = "{-# OPTIONS_GHC -Wmissing-signatures -Wmissing-pattern-synonym-signatures #-}"
1238− moduleH = "{-# LANGUAGE PatternSynonyms #-}\nmodule Sigs where"
981+ header = "{-# OPTIONS_GHC -Wmissing-signatures #-}"
982+ moduleH = "module Sigs where"
1239983 before def = T.unlines [header, moduleH, def]
1240984 after' def sig = T.unlines [header, moduleH, sig, def]
1241985
1242986 def >:: sig = testSession (T.unpack def) $ do
1243987 let originalCode = before def
1244988 let expectedCode = after' def sig
1245− doc <- createDoc "Sigs.hs" "haskell" originalCode
989+ doc <- openDoc' "Sigs.hs" "haskell" originalCode
1246990 _ <- waitForDiagnostics
1247991 actionsOrCommands <- getCodeActions doc (Range (Position 3 1) (Position 3 maxBound))
1248− chosenAction <- liftIO $ pickActionWithTitle ("add signature: " <> sig) actionsOrCommands
992+ let chosenAction = pickActionWithTitle ("add signature: " <> sig) actionsOrCommands
1249993 executeCodeAction chosenAction
1250994 modifiedCode <- documentContents doc
1251995 liftIO $ expectedCode @=? modifiedCode
1252996 in
1253997 testGroup "add signature"
1254− [ "abc = True" >:: "abc :: Bool"
1255− , "foo a b = a + b" >:: "foo :: Num a => a -> a -> a"
1256− , "bar a b = show $ a + b" >:: "bar :: (Show a, Num a) => a -> a -> String"
1257− , "(!!!) a b = a > b" >:: "(!!!) :: Ord a => a -> a -> Bool"
1258− , "a >>>> b = a + b" >:: "(>>>>) :: Num a => a -> a -> a"
1259− , "a `haha` b = a b" >:: "haha :: (t1 -> t2) -> t1 -> t2"
1260− , "pattern Some a = Just a" >:: "pattern Some :: a -> Maybe a"
998+ [ "abc = True" >:: "abc :: Bool"
999+ , "foo a b = a + b" >:: "foo :: Num a => a -> a -> a"
1000+ , "bar a b = show $ a + b" >:: "bar :: (Show a, Num a) => a -> a -> String"
1001+ , "(!!!) a b = a > b" >:: "(!!!) :: Ord a => a -> a -> Bool"
1002+ , "a >>>> b = a + b" >:: "(>>>>) :: Num a => a -> a -> a"
1003+ , "a `haha` b = a b" >:: "haha :: (t1 -> t2) -> t1 -> t2"
12611004 ]
12621005
12631006addSigLensesTests :: TestTree
12641007addSigLensesTests = let
1265− missing = "{-# OPTIONS_GHC -Wmissing-signatures -Wmissing-pattern-synonym-signatures -Wunused-matches #-}"
1008+ missing = "{-# OPTIONS_GHC -Wmissing-signatures -Wunused-matches #-}"
12661009 notMissing = "{-# OPTIONS_GHC -Wunused-matches #-}"
1267− moduleH = "{-# LANGUAGE PatternSynonyms #-}\nmodule Sigs where"
1010+ moduleH = "module Sigs where"
12681011 other = T.unlines ["f :: Integer -> Integer", "f x = 3"]
12691012 before withMissing def
12701013 = T.unlines $ (if withMissing then (missing :) else (notMissing :)) [moduleH, def, other]
@@ -1274,61 +1017,58 @@ addSigLensesTests = let
12741017 sigSession withMissing def sig = testSession (T.unpack def) $ do
12751018 let originalCode = before withMissing def
12761019 let expectedCode = after' withMissing def sig
1277− doc <- createDoc "Sigs.hs" "haskell" originalCode
1020+ doc <- openDoc' "Sigs.hs" "haskell" originalCode
12781021 [CodeLens {_command = Just c}] <- getCodeLenses doc
12791022 executeCommand c
12801023 modifiedCode <- getDocumentEdit doc
12811024 liftIO $ expectedCode @=? modifiedCode
12821025 in
12831026 testGroup "add signature"
1284− [ testGroup title
1285− [ sigSession enableWarnings "abc = True" "abc :: Bool"
1286− , sigSession enableWarnings "foo a b = a + b" "foo :: Num a => a -> a -> a"
1287− , sigSession enableWarnings "bar a b = show $ a + b" "bar :: (Show a, Num a) => a -> a -> String"
1288− , sigSession enableWarnings "(!!!) a b = a > b" "(!!!) :: Ord a => a -> a -> Bool"
1289− , sigSession enableWarnings "a >>>> b = a + b" "(>>>>) :: Num a => a -> a -> a"
1290− , sigSession enableWarnings "a `haha` b = a b" "haha :: (t1 -> t2) -> t1 -> t2"
1291− , sigSession enableWarnings "pattern Some a = Just a" "pattern Some :: a -> Maybe a"
1027+ [ testGroup "with warnings enabled"
1028+ [ sigSession True "abc = True" "abc :: Bool"
1029+ , sigSession True "foo a b = a + b" "foo :: Num a => a -> a -> a"
1030+ , sigSession True "bar a b = show $ a + b" "bar :: (Show a, Num a) => a -> a -> String"
1031+ , sigSession True "(!!!) a b = a > b" "(!!!) :: Ord a => a -> a -> Bool"
1032+ , sigSession True "a >>>> b = a + b" "(>>>>) :: Num a => a -> a -> a"
1033+ , sigSession True "a `haha` b = a b" "haha :: (t1 -> t2) -> t1 -> t2"
1034+ ]
1035+ , testGroup "with warnings disabled"
1036+ [ sigSession False "abc = True" "abc :: Bool"
1037+ , sigSession False "foo a b = a + b" "foo :: Num a => a -> a -> a"
1038+ , sigSession False "bar a b = show $ a + b" "bar :: (Show a, Num a) => a -> a -> String"
1039+ , sigSession False "(!!!) a b = a > b" "(!!!) :: Ord a => a -> a -> Bool"
1040+ , sigSession False "a >>>> b = a + b" "(>>>>) :: Num a => a -> a -> a"
1041+ , sigSession False "a `haha` b = a b" "haha :: (t1 -> t2) -> t1 -> t2"
12921042 ]
1293− | (title, enableWarnings) <-
1294− [("with warnings enabled", True)
1295− ,("with warnings disabled", False)
1296− ]
12971043 ]
12981044
1299−checkDefs :: [Location] -> Session [Expect] -> Session ()
1300−checkDefs defs mkExpectations = traverse_ check =<< mkExpectations where
1301−
1302− check (ExpectRange expectedRange) = do
1303− assertNDefinitionsFound 1 defs
1304− assertRangeCorrect (head defs) expectedRange
1305− check (ExpectLocation expectedLocation) = do
1306− assertNDefinitionsFound 1 defs
1307− liftIO $ head defs @?= expectedLocation
1308− check ExpectNoDefinitions = do
1309− assertNDefinitionsFound 0 defs
1310− check ExpectExternFail = liftIO $ assertFailure "Expecting to fail to find in external file"
1311− check _ = pure () -- all other expectations not relevant to getDefinition
1312−
1313− assertNDefinitionsFound :: Int -> [a] -> Session ()
1314− assertNDefinitionsFound n defs = liftIO $ assertEqual "number of definitions" n (length defs)
1315−
1316− assertRangeCorrect Location{_range = foundRange} expectedRange =
1317− liftIO $ expectedRange @=? foundRange
1318−
1319−
13201045findDefinitionAndHoverTests :: TestTree
13211046findDefinitionAndHoverTests = let
13221047
1323− tst (get, check) pos targetRange title = testSessionWithExtraFiles "hover" title $ \dir -> do
1324− doc <- openTestDataDoc (dir </> sourceFilePath)
1048+ tst (get, check) pos targetRange title = testSession title $ do
1049+ doc <- openTestDataDoc sourceFilePath
13251050 found <- get doc pos
13261051 check found targetRange
13271052
1053+ checkDefs :: [Location] -> [Expect] -> Session ()
1054+ checkDefs defs expectations = traverse_ check expectations where
13281055
1056+ check (ExpectRange expectedRange) = do
1057+ assertNDefinitionsFound 1 defs
1058+ assertRangeCorrect (head defs) expectedRange
1059+ check ExpectNoDefinitions = do
1060+ assertNDefinitionsFound 0 defs
1061+ check ExpectExternFail = liftIO $ assertFailure "Expecting to fail to find in external file"
1062+ check _ = pure () -- all other expectations not relevant to getDefinition
13291063
1330− checkHover :: Maybe Hover -> Session [Expect] -> Session ()
1331− checkHover hover expectations = traverse_ check =<< expectations where
1064+ assertNDefinitionsFound :: Int -> [a] -> Session ()
1065+ assertNDefinitionsFound n defs = liftIO $ assertEqual "number of definitions" n (length defs)
1066+
1067+ assertRangeCorrect Location{_range = foundRange} expectedRange =
1068+ liftIO $ expectedRange @=? foundRange
1069+
1070+ checkHover :: Maybe Hover -> [Expect] -> Session ()
1071+ checkHover hover expectations = traverse_ check expectations where
13321072
13331073 check expected =
13341074 case hover of
@@ -1371,12 +1111,9 @@ findDefinitionAndHoverTests = let
13711111
13721112 mkFindTests tests = testGroup "get"
13731113 [ testGroup "definition" $ mapMaybe fst tests
1374− , testGroup "hover" $ mapMaybe snd tests
1375− , checkFileCompiles sourceFilePath ]
1376−
1377− test runDef runHover look expect = testM runDef runHover look (return expect)
1114+ , testGroup "hover" $ mapMaybe snd tests ]
13781115
1379− testM runDef runHover look expect title =
1116+ test runDef runHover look expect title =
13801117 ( runDef $ tst def look expect title
13811118 , runHover $ tst hover look expect title ) where
13821119 def = (getDefinitions, checkDefs)
@@ -1384,100 +1121,87 @@ findDefinitionAndHoverTests = let
13841121 --type_ = (getTypeDefinitions, checkTDefs) -- getTypeDefinitions always times out
13851122
13861123 -- search locations expectations on results
1387− fffL4 = _start fffR ; fffR = mkRange 8 4 8 7 ; fff = [ExpectRange fffR]
1388− fffL8 = Position 12 4 ;
1389− fffL14 = Position 18 7 ;
1390− aaaL14 = Position 18 20 ; aaa = [mkR 11 0 11 3]
1391− dcL7 = Position 11 11 ; tcDC = [mkR 7 23 9 16]
1392− dcL12 = Position 16 11 ;
1393− xtcL5 = Position 9 11 ; xtc = [ExpectExternFail, ExpectHoverText ["Int", "Defined in ‘GHC.Types’"]]
1394− tcL6 = Position 10 11 ; tcData = [mkR 7 0 9 16, ExpectHoverText ["TypeConstructor", "GotoHover.hs:8:1"]]
1395− vvL16 = Position 20 12 ; vv = [mkR 20 4 20 6]
1396− opL16 = Position 20 15 ; op = [mkR 21 2 21 4]
1397− opL18 = Position 22 22 ; opp = [mkR 22 13 22 17]
1398− aL18 = Position 22 20 ; apmp = [mkR 22 10 22 11]
1399− b'L19 = Position 23 13 ; bp = [mkR 23 6 23 7]
1400− xvL20 = Position 24 8 ; xvMsg = [ExpectExternFail, ExpectHoverText ["Data.Text.pack", ":: String -> Text"]]
1401− clL23 = Position 27 11 ; cls = [mkR 25 0 26 20, ExpectHoverText ["MyClass", "GotoHover.hs:26:1"]]
1402− clL25 = Position 29 9
1403− eclL15 = Position 19 8 ; ecls = [ExpectExternFail, ExpectHoverText ["Num", "Defined in ‘GHC.Num’"]]
1404− dnbL29 = Position 33 18 ; dnb = [ExpectHoverText [":: ()"], mkR 33 12 33 21]
1405− dnbL30 = Position 34 23
1406− lcbL33 = Position 37 26 ; lcb = [ExpectHoverText [":: Char"], mkR 37 26 37 27]
1407− lclL33 = Position 37 22
1408− mclL36 = Position 40 1 ; mcl = [mkR 40 0 40 14]
1409− mclL37 = Position 41 1
1410− spaceL37 = Position 41 24 ; space = [ExpectNoDefinitions, ExpectHoverText [":: Char"]]
1411− docL41 = Position 45 1 ; doc = [ExpectHoverText ["Recognizable docs: kpqz"]]
1124+ fffL4 = _start fffR ; fffR = mkRange 4 4 4 7 ; fff = [ExpectRange fffR]
1125+ fffL8 = Position 8 4 ;
1126+ fffL14 = Position 14 7 ;
1127+ aaaL14 = Position 14 20 ; aaa = [mkR 7 0 7 3]
1128+ dcL7 = Position 7 11 ; tcDC = [mkR 3 23 5 16]
1129+ dcL12 = Position 12 11 ;
1130+ xtcL5 = Position 5 11 ; xtc = [ExpectExternFail, ExpectHoverText ["Int", "Defined in ‘GHC.Types’"]]
1131+ tcL6 = Position 6 11 ; tcData = [mkR 3 0 5 16, ExpectHoverText ["TypeConstructor", "GotoHover.hs:4:1"]]
1132+ vvL16 = Position 16 12 ; vv = [mkR 16 4 16 6]
1133+ opL16 = Position 16 15 ; op = [mkR 17 2 17 4]
1134+ opL18 = Position 18 22 ; opp = [mkR 18 13 18 17]
1135+ aL18 = Position 18 20 ; apmp = [mkR 18 10 18 11]
1136+ b'L19 = Position 19 13 ; bp = [mkR 19 6 19 7]
1137+ xvL20 = Position 20 8 ; xvMsg = [ExpectExternFail, ExpectHoverText ["Data.Text.pack", ":: String -> Text"]]
1138+ clL23 = Position 23 11 ; cls = [mkR 21 0 22 20, ExpectHoverText ["MyClass", "GotoHover.hs:22:1"]]
1139+ clL25 = Position 25 9
1140+ eclL15 = Position 15 8 ; ecls = [ExpectExternFail, ExpectHoverText ["Num", "Defined in ‘GHC.Num’"]]
1141+ dnbL29 = Position 29 18 ; dnb = [ExpectHoverText [":: ()"], mkR 29 12 29 21]
1142+ dnbL30 = Position 30 23
1143+ lcbL33 = Position 33 26 ; lcb = [ExpectHoverText [":: Char"], mkR 33 26 33 27]
1144+ lclL33 = Position 33 22
1145+ mclL36 = Position 36 1 ; mcl = [mkR 36 0 36 14]
1146+ mclL37 = Position 37 1
1147+ spaceL37 = Position 37 24 ; space = [ExpectNoDefinitions, ExpectHoverText [":: Char"]]
1148+ docL41 = Position 41 1 ; doc = [ExpectHoverText ["Recognizable docs: kpqz"]]
14121149 ; constr = [ExpectHoverText ["Monad m"]]
1413− eitL40 = Position 44 28 ; kindE = [ExpectHoverText [":: * -> * -> *\n"]]
1414− intL40 = Position 44 34 ; kindI = [ExpectHoverText [":: *\n"]]
1415− tvrL40 = Position 44 37 ; kindV = [ExpectHoverText [":: * -> *\n"]]
1416− intL41 = Position 45 20 ; litI = [ExpectHoverText ["7518"]]
1417− chrL36 = Position 41 24 ; litC = [ExpectHoverText ["'f'"]]
1418− txtL8 = Position 12 14 ; litT = [ExpectHoverText ["\"dfgy\""]]
1419− lstL43 = Position 47 12 ; litL = [ExpectHoverText ["[8391 :: Int, 6268]"]]
1420− outL45 = Position 49 3 ; outSig = [ExpectHoverText ["outer", "Bool"], mkR 46 0 46 5]
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]
1150+ eitL40 = Position 40 28 ; kindE = [ExpectHoverText [":: * -> * -> *\n"]]
1151+ intL40 = Position 40 34 ; kindI = [ExpectHoverText [":: *\n"]]
1152+ tvrL40 = Position 40 37 ; kindV = [ExpectHoverText [":: * -> *\n"]]
1153+ intL41 = Position 41 20 ; litI = [ExpectHoverText ["7518"]]
1154+ chrL36 = Position 37 24 ; litC = [ExpectHoverText ["'f'"]]
1155+ txtL8 = Position 8 14 ; litT = [ExpectHoverText ["\"dfgv\""]]
1156+ lstL43 = Position 43 12 ; litL = [ExpectHoverText ["[ 8391 :: Int, 6268 ]"]]
1157+ outL45 = Position 45 3 ; outSig = [ExpectHoverText ["outer", "Bool"], mkR 46 0 46 5]
1158+ innL48 = Position 48 5 ; innSig = [ExpectHoverText ["inner", "Char"], mkR 49 2 49 7]
14241159 in
14251160 mkFindTests
1426− -- def hover look expect
1427− [ test yes yes fffL4 fff "field in record definition"
1428− , test broken broken fffL8 fff "field in record construction #71"
1429− , test yes yes fffL14 fff "field name used as accessor" -- 120 in Calculate.hs
1430− , test yes yes aaaL14 aaa "top-level name" -- 120
1431− , test yes yes dcL7 tcDC "data constructor record #247"
1432− , test yes yes dcL12 tcDC "data constructor plain" -- 121
1433− , test yes yes tcL6 tcData "type constructor #248" -- 147
1434− , test broken yes xtcL5 xtc "type constructor external #248,249"
1435− , test broken yes xvL20 xvMsg "value external package #249" -- 120
1436− , test yes yes vvL16 vv "plain parameter" -- 120
1437− , test yes yes aL18 apmp "pattern match name" -- 120
1438− , test yes yes opL16 op "top-level operator" -- 120, 123
1439− , test yes yes opL18 opp "parameter operator" -- 120
1440− , test yes yes b'L19 bp "name in backticks" -- 120
1441− , test yes yes clL23 cls "class in instance declaration #250"
1442− , test yes yes clL25 cls "class in signature #250" -- 147
1443− , test broken yes eclL15 ecls "external class in signature #249,250"
1444− , test yes yes dnbL29 dnb "do-notation bind" -- 137
1445− , test yes yes dnbL30 dnb "do-notation lookup"
1446− , test yes yes lcbL33 lcb "listcomp bind" -- 137
1447− , test yes yes lclL33 lcb "listcomp lookup"
1448− , test yes yes mclL36 mcl "top-level fn 1st clause"
1449− , test yes yes mclL37 mcl "top-level fn 2nd clause #246"
1450− , test yes yes spaceL37 space "top-level fn on space #315"
1451− , test no broken docL41 doc "documentation #7"
1452− , test no yes eitL40 kindE "kind of Either #273"
1453− , test no yes intL40 kindI "kind of Int #273"
1454− , test no broken tvrL40 kindV "kind of (* -> *) type variable #273"
1455− , test no yes intL41 litI "literal Int in hover info #274"
1456− , test no yes chrL36 litC "literal Char in hover info #274"
1457− , test no yes txtL8 litT "literal Text in hover info #274"
1458− , test no yes lstL43 litL "literal List in hover info #274"
1459− , test no yes docL41 constr "type constraint in hover info #283"
1460− , test broken broken outL45 outSig "top-level 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)"
1161+ -- def hover look expect
1162+ [ test yes yes fffL4 fff "field in record definition"
1163+ , test broken broken fffL8 fff "field in record construction #71"
1164+ , test yes yes fffL14 fff "field name used as accessor" -- 120 in Calculate.hs
1165+ , test yes yes aaaL14 aaa "top-level name" -- 120
1166+ , test yes yes dcL7 tcDC "data constructor record #247"
1167+ , test yes yes dcL12 tcDC "data constructor plain" -- 121
1168+ , test yes yes tcL6 tcData "type constructor #248" -- 147
1169+ , test broken yes xtcL5 xtc "type constructor external #248,249"
1170+ , test broken yes xvL20 xvMsg "value external package #249" -- 120
1171+ , test yes yes vvL16 vv "plain parameter" -- 120
1172+ , test yes yes aL18 apmp "pattern match name" -- 120
1173+ , test yes yes opL16 op "top-level operator" -- 120, 123
1174+ , test yes yes opL18 opp "parameter operator" -- 120
1175+ , test yes yes b'L19 bp "name in backticks" -- 120
1176+ , test yes yes clL23 cls "class in instance declaration #250"
1177+ , test yes yes clL25 cls "class in signature #250" -- 147
1178+ , test broken yes eclL15 ecls "external class in signature #249,250"
1179+ , test yes yes dnbL29 dnb "do-notation bind" -- 137
1180+ , test yes yes dnbL30 dnb "do-notation lookup"
1181+ , test yes yes lcbL33 lcb "listcomp bind" -- 137
1182+ , test yes yes lclL33 lcb "listcomp lookup"
1183+ , test yes yes mclL36 mcl "top-level fn 1st clause"
1184+ , test yes yes mclL37 mcl "top-level fn 2nd clause #246"
1185+ , test yes yes spaceL37 space "top-level fn on space #315"
1186+ , test no broken docL41 doc "documentation #7"
1187+ , test no yes eitL40 kindE "kind of Either #273"
1188+ , test no yes intL40 kindI "kind of Int #273"
1189+ , test no broken tvrL40 kindV "kind of (* -> *) type variable #273"
1190+ , test no yes intL41 litI "literal Int in hover info #274"
1191+ , test no yes chrL36 litC "literal Char in hover info #274"
1192+ , test no broken txtL8 litT "literal Text in hover info #274"
1193+ , test no broken lstL43 litL "literal List in hover info #274"
1194+ , test no yes docL41 constr "type constraint in hover info #283"
1195+ , test broken broken outL45 outSig "top-level signature #310"
1196+ , test broken broken innL48 innSig "inner signature #310"
14641197 ]
14651198 where yes, broken :: (TestTree -> Maybe TestTree)
14661199 yes = Just -- test should run and pass
14671200 broken = Just . (`xfail` "known broken")
14681201 no = const Nothing -- don't run this test at all
14691202
1470−checkFileCompiles :: FilePath -> TestTree
1471−checkFileCompiles fp =
1472− testSessionWithExtraFiles "hover" ("Does " ++ fp ++ " compile") $ \dir -> do
1473− void (openTestDataDoc (dir </> fp))
1474− expectNoMoreDiagnostics 0.5
1475−
1476−
1477−
14781203pluginTests :: TestTree
1479−pluginTests = (`xfail8101` "known broken (#556)")
1480− $ testSessionWait "plugins" $ do
1204+pluginTests = testSessionWait "plugins" $ do
14811205 let content =
14821206 T.unlines
14831207 [ "{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}"
@@ -1491,7 +1215,7 @@ pluginTests = (`xfail8101` "known broken (#556)")
14911215 , "foo :: Int -> Int -> Int"
14921216 , "foo a b = a + c"
14931217 ]
1494− _ <- createDoc "Testing.hs" "haskell" content
1218+ _ <- openDoc' "Testing.hs" "haskell" content
14951219 expectDiagnostics
14961220 [ ( "Testing.hs",
14971221 [(DsError, (8, 14), "Variable not in scope: c")]
@@ -1500,39 +1224,26 @@ pluginTests = (`xfail8101` "known broken (#556)")
15001224
15011225cppTests :: TestTree
15021226cppTests =
1503− testGroup "cpp"
1504− [ testCase "cpp-error" $ do
1505− let content =
1506− T.unlines
1507− [ "{-# LANGUAGE CPP #-}",
1508− "module Testing where",
1509− "#ifdef FOO",
1510− "foo = 42"
1511− ]
1512− -- The error locations differ depending on which C-preprocessor is used.
1513− -- Some give the column number and others don't (hence -1). Assert either
1514− -- of them.
1515− (run $ expectError content (2, -1))
1516− `catch` ( \e -> do
1517− let _ = e :: HUnitFailure
1518− run $ expectError content (2, 1)
1519− )
1520− , testSessionWait "cpp-ghcide" $ do
1521− _ <- createDoc "A.hs" "haskell" $ T.unlines
1522− ["{-# LANGUAGE CPP #-}"
1523− ,"main ="
1524− ,"#ifdef __GHCIDE__"
1525− ," worked"
1526− ,"#else"
1527− ," failed"
1528− ,"#endif"
1529− ]
1530− expectDiagnostics [("A.hs", [(DsError, (3, 2), "Variable not in scope: worked")])]
1531− ]
1227+ testCase "cpp" $ do
1228+ let content =
1229+ T.unlines
1230+ [ "{-# LANGUAGE CPP #-}",
1231+ "module Testing where",
1232+ "#ifdef FOO",
1233+ "foo = 42"
1234+ ]
1235+ -- The error locations differ depending on which C-preprocessor is used.
1236+ -- Some give the column number and others don't (hence -1). Assert either
1237+ -- of them.
1238+ (run $ expectError content (2, -1))
1239+ `catch` ( \e -> do
1240+ let _ = e :: HUnitFailure
1241+ run $ expectError content (2, 1)
1242+ )
15321243 where
15331244 expectError :: T.Text -> Cursor -> Session ()
15341245 expectError content cursor = do
1535− _ <- createDoc "Testing.hs" "haskell" content
1246+ _ <- openDoc' "Testing.hs" "haskell" content
15361247 expectDiagnostics
15371248 [ ( "Testing.hs",
15381249 [(DsError, cursor, "error: unterminated")]
@@ -1548,43 +1259,13 @@ preprocessorTests = testSessionWait "preprocessor" $ do
15481259 , "module Testing where"
15491260 , "y = x + z" -- plugin replaces x with y, making this have only one diagnostic
15501261 ]
1551− _ <- createDoc "Testing.hs" "haskell" content
1262+ _ <- openDoc' "Testing.hs" "haskell" content
15521263 expectDiagnostics
15531264 [ ( "Testing.hs",
15541265 [(DsError, (2, 8), "Variable not in scope: z")]
15551266 )
15561267 ]
15571268
1558−
1559−safeTests :: TestTree
1560−safeTests =
1561− testGroup
1562− "SafeHaskell"
1563− [ -- Test for https://github.com/digital-asset/ghcide/issues/424
1564− testSessionWait "load" $ do
1565− let sourceA =
1566− T.unlines
1567− ["{-# LANGUAGE Trustworthy #-}"
1568− ,"module A where"
1569− ,"import System.IO.Unsafe"
1570− ,"import System.IO"
1571− ,"trustWorthyId :: a -> a"
1572− ,"trustWorthyId i = unsafePerformIO $ do"
1573− ," putStrLn \"I'm safe\""
1574− ," return i"]
1575− sourceB =
1576− T.unlines
1577− ["{-# LANGUAGE Safe #-}"
1578− ,"module B where"
1579− ,"import A"
1580− ,"safeId :: a -> a"
1581− ,"safeId = trustWorthyId"
1582− ]
1583−
1584− _ <- createDoc "A.hs" "haskell" sourceA
1585− _ <- createDoc "B.hs" "haskell" sourceB
1586− expectNoMoreDiagnostics 1 ]
1587−
15881269thTests :: TestTree
15891270thTests =
15901271 testGroup
@@ -1610,30 +1291,9 @@ thTests =
16101291 "b :: Integer",
16111292 "b = $(litE $ IntegerL $ a) + n"
16121293 ]
1613− _ <- createDoc "A.hs" "haskell" sourceA
1614− _ <- createDoc "B.hs" "haskell" sourceB
1294+ _ <- openDoc' "A.hs" "haskell" sourceA
1295+ _ <- openDoc' "B.hs" "haskell" sourceB
16151296 expectDiagnostics [ ( "B.hs", [(DsError, (6, 29), "Variable not in scope: n")] ) ]
1616− , testSessionWait "newtype-closure" $ do
1617− let sourceA =
1618− T.unlines
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)"
1625− ,"a :: ExpQ"
1626− ,"a = [| 0 |]"]
1627− let sourceB =
1628− T.unlines
1629− [ "{-# LANGUAGE TemplateHaskell #-}"
1630− ,"module B where"
1631− ,"import A"
1632− ,"b :: Int"
1633− ,"b = $( a )" ]
1634− _ <- createDoc "A.hs" "haskell" sourceA
1635− _ <- createDoc "B.hs" "haskell" sourceB
1636− return ()
16371297 ]
16381298
16391299completionTests :: TestTree
@@ -1641,31 +1301,31 @@ completionTests
16411301 = testGroup "completion"
16421302 [ testSessionWait "variable" $ do
16431303 let source = T.unlines ["module A where", "f = hea"]
1644− docId <- createDoc "A.hs" "haskell" source
1304+ docId <- openDoc' "A.hs" "haskell" source
16451305 compls <- getCompletions docId (Position 1 7)
1646− liftIO $ map dropDocs compls @?=
1306+ liftIO $ map dropDocs compls @?=
16471307 [complItem "head" (Just CiFunction) (Just "[a] -> a")]
16481308 let [CompletionItem { _documentation = headDocs}] = compls
16491309 checkDocText "head" headDocs [ "Defined in 'Prelude'"
1650−#if MIN_GHC_API_VERSION(8,6,5)
1310+#if MIN_GHC_API_VERSION(8,6,0)
16511311 , "Extract the first element of a list"
16521312#endif
16531313 ]
16541314 , testSessionWait "constructor" $ do
16551315 let source = T.unlines ["module A where", "f = Tru"]
1656− docId <- createDoc "A.hs" "haskell" source
1316+ docId <- openDoc' "A.hs" "haskell" source
16571317 compls <- getCompletions docId (Position 1 7)
1658− liftIO $ map dropDocs compls @?=
1318+ liftIO $ map dropDocs compls @?=
16591319 [ complItem "True" (Just CiConstructor) (Just "Bool")
16601320#if MIN_GHC_API_VERSION(8,6,0)
16611321 , complItem "truncate" (Just CiFunction) (Just "(RealFrac a, Integral b) => a -> b")
16621322#else
1663− , complItem "truncate" (Just CiFunction) (Just "RealFrac a => forall b. Integral b => a -> b")
1323+ , complItem "truncate" (Just CiFunction) (Just "RealFrac a => forall b. Integral b => a -> b")
16641324#endif
16651325 ]
16661326 , testSessionWait "type" $ do
16671327 let source = T.unlines ["{-# OPTIONS_GHC -Wall #-}", "module A () where", "f :: ()", "f = ()"]
1668− docId <- createDoc "A.hs" "haskell" source
1328+ docId <- openDoc' "A.hs" "haskell" source
16691329 expectDiagnostics [ ("A.hs", [(DsWarning, (3,0), "not used")]) ]
16701330 changeDoc docId [TextDocumentContentChangeEvent Nothing Nothing $ T.unlines ["{-# OPTIONS_GHC -Wall #-}", "module A () where", "f :: Bo", "f = True"]]
16711331 compls <- getCompletions docId (Position 2 7)
@@ -1675,51 +1335,30 @@ completionTests
16751335 let [ CompletionItem { _documentation = boundedDocs},
16761336 CompletionItem { _documentation = boolDocs } ] = compls
16771337 checkDocText "Bounded" boundedDocs [ "Defined in 'Prelude'"
1678−#if MIN_GHC_API_VERSION(8,6,5)
1338+#if MIN_GHC_API_VERSION(8,6,0)
16791339 , "name the upper and lower limits"
16801340#endif
16811341 ]
16821342 checkDocText "Bool" boolDocs [ "Defined in 'Prelude'" ]
16831343 , testSessionWait "qualified" $ do
16841344 let source = T.unlines ["{-# OPTIONS_GHC -Wunused-binds #-}", "module A () where", "f = ()"]
1685− docId <- createDoc "A.hs" "haskell" source
1345+ docId <- openDoc' "A.hs" "haskell" source
16861346 expectDiagnostics [ ("A.hs", [(DsWarning, (2, 0), "not used")]) ]
16871347 changeDoc docId [TextDocumentContentChangeEvent Nothing Nothing $ T.unlines ["{-# OPTIONS_GHC -Wunused-binds #-}", "module A () where", "f = Prelude.hea"]]
16881348 compls <- getCompletions docId (Position 2 15)
1689− liftIO $ map dropDocs compls @?=
1349+ liftIO $ map dropDocs compls @?=
16901350 [complItem "head" (Just CiFunction) (Just "[a] -> a")]
16911351 let [CompletionItem { _documentation = headDocs}] = compls
16921352 checkDocText "head" headDocs [ "Defined in 'Prelude'"
1693−#if MIN_GHC_API_VERSION(8,6,5)
1353+#if MIN_GHC_API_VERSION(8,6,0)
16941354 , "Extract the first element of a list"
16951355#endif
16961356 ]
16971357 , testSessionWait "keyword" $ do
16981358 let source = T.unlines ["module A where", "f = newty"]
1699− docId <- createDoc "A.hs" "haskell" source
1359+ docId <- openDoc' "A.hs" "haskell" source
17001360 compls <- getCompletions docId (Position 1 9)
17011361 liftIO $ compls @?= [keywordItem "newtype"]
1702− , testSessionWait "type context" $ do
1703− let source = T.unlines
1704− [ "{-# OPTIONS_GHC -Wunused-binds #-}"
1705− , "module A () where"
1706− , "f = f"
1707− ]
1708− docId <- createDoc "A.hs" "haskell" source
1709− expectDiagnostics [("A.hs", [(DsWarning, (2, 0), "not used")])]
1710− changeDoc docId
1711− [ TextDocumentContentChangeEvent Nothing Nothing $ T.unlines
1712− [ "{-# OPTIONS_GHC -Wunused-binds #-}"
1713− , "module A () where"
1714− , "f = f"
1715− , "g :: Intege"
1716− ]
1717− ]
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 "*")]
17231362 ]
17241363 where
17251364 dropDocs :: CompletionItem -> CompletionItem
@@ -1727,7 +1366,6 @@ completionTests
17271366 complItem label kind ty = CompletionItem
17281367 { _label = label
17291368 , _kind = kind
1730− , _tags = List []
17311369 , _detail = (":: " <>) <$> ty
17321370 , _documentation = Nothing
17331371 , _deprecated = Nothing
@@ -1745,7 +1383,6 @@ completionTests
17451383 keywordItem label = CompletionItem
17461384 { _label = label
17471385 , _kind = Just CiKeyword
1748− , _tags = List []
17491386 , _detail = Nothing
17501387 , _documentation = Nothing
17511388 , _deprecated = Nothing
@@ -1773,7 +1410,7 @@ outlineTests = testGroup
17731410 "outline"
17741411 [ testSessionWait "type class" $ do
17751412 let source = T.unlines ["module A where", "class A a where a :: a -> Bool"]
1776− docId <- createDoc "A.hs" "haskell" source
1413+ docId <- openDoc' "A.hs" "haskell" source
17771414 symbols <- getDocumentSymbols docId
17781415 liftIO $ symbols @?= Left
17791416 [ moduleSymbol
@@ -1786,7 +1423,7 @@ outlineTests = testGroup
17861423 ]
17871424 , testSessionWait "type class instance " $ do
17881425 let source = T.unlines ["class A a where", "instance A () where"]
1789− docId <- createDoc "A.hs" "haskell" source
1426+ docId <- openDoc' "A.hs" "haskell" source
17901427 symbols <- getDocumentSymbols docId
17911428 liftIO $ symbols @?= Left
17921429 [ classSymbol "A a" (R 0 0 0 15) []
@@ -1794,7 +1431,7 @@ outlineTests = testGroup
17941431 ]
17951432 , testSessionWait "type family" $ do
17961433 let source = T.unlines ["{-# language TypeFamilies #-}", "type family A"]
1797− docId <- createDoc "A.hs" "haskell" source
1434+ docId <- openDoc' "A.hs" "haskell" source
17981435 symbols <- getDocumentSymbols docId
17991436 liftIO $ symbols @?= Left [docSymbolD "A" "type family" SkClass (R 1 0 1 13)]
18001437 , testSessionWait "type family instance " $ do
@@ -1803,7 +1440,7 @@ outlineTests = testGroup
18031440 , "type family A a"
18041441 , "type instance A () = ()"
18051442 ]
1806− docId <- createDoc "A.hs" "haskell" source
1443+ docId <- openDoc' "A.hs" "haskell" source
18071444 symbols <- getDocumentSymbols docId
18081445 liftIO $ symbols @?= Left
18091446 [ docSymbolD "A a" "type family" SkClass (R 1 0 1 15)
@@ -1811,7 +1448,7 @@ outlineTests = testGroup
18111448 ]
18121449 , testSessionWait "data family" $ do
18131450 let source = T.unlines ["{-# language TypeFamilies #-}", "data family A"]
1814− docId <- createDoc "A.hs" "haskell" source
1451+ docId <- openDoc' "A.hs" "haskell" source
18151452 symbols <- getDocumentSymbols docId
18161453 liftIO $ symbols @?= Left [docSymbolD "A" "data family" SkClass (R 1 0 1 11)]
18171454 , testSessionWait "data family instance " $ do
@@ -1820,7 +1457,7 @@ outlineTests = testGroup
18201457 , "data family A a"
18211458 , "data instance A () = A ()"
18221459 ]
1823− docId <- createDoc "A.hs" "haskell" source
1460+ docId <- openDoc' "A.hs" "haskell" source
18241461 symbols <- getDocumentSymbols docId
18251462 liftIO $ symbols @?= Left
18261463 [ docSymbolD "A a" "data family" SkClass (R 1 0 1 11)
@@ -1828,36 +1465,36 @@ outlineTests = testGroup
18281465 ]
18291466 , testSessionWait "constant" $ do
18301467 let source = T.unlines ["a = ()"]
1831− docId <- createDoc "A.hs" "haskell" source
1468+ docId <- openDoc' "A.hs" "haskell" source
18321469 symbols <- getDocumentSymbols docId
18331470 liftIO $ symbols @?= Left
18341471 [docSymbol "a" SkFunction (R 0 0 0 6)]
18351472 , testSessionWait "pattern" $ do
18361473 let source = T.unlines ["Just foo = Just 21"]
1837− docId <- createDoc "A.hs" "haskell" source
1474+ docId <- openDoc' "A.hs" "haskell" source
18381475 symbols <- getDocumentSymbols docId
18391476 liftIO $ symbols @?= Left
18401477 [docSymbol "Just foo" SkFunction (R 0 0 0 18)]
18411478 , testSessionWait "pattern with type signature" $ do
18421479 let source = T.unlines ["{-# language ScopedTypeVariables #-}", "a :: () = ()"]
1843− docId <- createDoc "A.hs" "haskell" source
1480+ docId <- openDoc' "A.hs" "haskell" source
18441481 symbols <- getDocumentSymbols docId
18451482 liftIO $ symbols @?= Left
18461483 [docSymbol "a :: ()" SkFunction (R 1 0 1 12)]
18471484 , testSessionWait "function" $ do
18481485 let source = T.unlines ["a x = ()"]
1849− docId <- createDoc "A.hs" "haskell" source
1486+ docId <- openDoc' "A.hs" "haskell" source
18501487 symbols <- getDocumentSymbols docId
18511488 liftIO $ symbols @?= Left [docSymbol "a" SkFunction (R 0 0 0 8)]
18521489 , testSessionWait "type synonym" $ do
18531490 let source = T.unlines ["type A = Bool"]
1854− docId <- createDoc "A.hs" "haskell" source
1491+ docId <- openDoc' "A.hs" "haskell" source
18551492 symbols <- getDocumentSymbols docId
18561493 liftIO $ symbols @?= Left
18571494 [docSymbol' "A" SkTypeParameter (R 0 0 0 13) (R 0 5 0 6)]
18581495 , testSessionWait "datatype" $ do
18591496 let source = T.unlines ["data A = C"]
1860− docId <- createDoc "A.hs" "haskell" source
1497+ docId <- openDoc' "A.hs" "haskell" source
18611498 symbols <- getDocumentSymbols docId
18621499 liftIO $ symbols @?= Left
18631500 [ docSymbolWithChildren "A"
@@ -1865,47 +1502,18 @@ outlineTests = testGroup
18651502 (R 0 0 0 10)
18661503 [docSymbol "C" SkConstructor (R 0 9 0 10)]
18671504 ]
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)
1877− ]
1878− ]
1879− ]
18801505 , testSessionWait "import" $ do
18811506 let source = T.unlines ["import Data.Maybe"]
1882− docId <- createDoc "A.hs" "haskell" source
1507+ docId <- openDoc' "A.hs" "haskell" source
18831508 symbols <- getDocumentSymbols docId
18841509 liftIO $ symbols @?= Left
1885− [docSymbolWithChildren "imports"
1886− SkModule
1887− (R 0 0 0 17)
1888− [ docSymbol "import Data.Maybe" SkModule (R 0 0 0 17)
1889− ]
1890− ]
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"
1897− SkModule
1898− (R 1 0 3 24)
1899− [ docSymbol "import Data.Maybe" SkModule (R 1 0 1 17)
1900− , docSymbol "import Control.Exception" SkModule (R 3 0 3 24)
1901− ]
1902− ]
1510+ [docSymbol "import Data.Maybe" SkModule (R 0 0 0 17)]
19031511 , testSessionWait "foreign import" $ do
19041512 let source = T.unlines
19051513 [ "{-# language ForeignFunctionInterface #-}"
19061514 , "foreign import ccall \"a\" a :: Int"
19071515 ]
1908− docId <- createDoc "A.hs" "haskell" source
1516+ docId <- openDoc' "A.hs" "haskell" source
19091517 symbols <- getDocumentSymbols docId
19101518 liftIO $ symbols @?= Left [docSymbolD "a" "import" SkObject (R 1 0 1 33)]
19111519 , testSessionWait "foreign export" $ do
@@ -1913,7 +1521,7 @@ outlineTests = testGroup
19131521 [ "{-# language ForeignFunctionInterface #-}"
19141522 , "foreign export ccall odd :: Int -> Bool"
19151523 ]
1916− docId <- createDoc "A.hs" "haskell" source
1524+ docId <- openDoc' "A.hs" "haskell" source
19171525 symbols <- getDocumentSymbols docId
19181526 liftIO $ symbols @?= Left [docSymbolD "odd" "export" SkObject (R 1 0 1 39)]
19191527 ]
@@ -1926,8 +1534,6 @@ outlineTests = testGroup
19261534 DocumentSymbol name (Just detail) kind Nothing loc loc Nothing
19271535 docSymbolWithChildren name kind loc cc =
19281536 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)
19311537 moduleSymbol name loc cc = DocumentSymbol name
19321538 Nothing
19331539 SkFile
@@ -1949,16 +1555,8 @@ pattern R x y x' y' = Range (Position x y) (Position x' y')
19491555xfail :: TestTree -> String -> TestTree
19501556xfail = flip expectFailBecause
19511557
1952−xfail8101 :: TestTree -> String -> TestTree
1953−#if MIN_GHC_API_VERSION(8,10,0)
1954−xfail8101 = flip expectFailBecause
1955−#else
1956−xfail8101 t _ = t
1957−#endif
1958−
19591558data Expect
19601559 = ExpectRange Range -- Both gotoDef and hover should report this range
1961− | ExpectLocation Location
19621560-- | ExpectDefRange Range -- Only gotoDef should report this range
19631561 | ExpectHoverRange Range -- Only hover should report this range
19641562 | ExpectHoverText [T.Text] -- the hover message must contain these snippets
@@ -1970,226 +1568,6 @@ data Expect
19701568
19711569mkR :: Int -> Int -> Int -> Int -> Expect
19721570mkR startLine startColumn endLine endColumn = ExpectRange $ mkRange startLine startColumn endLine endColumn
1973−
1974−mkL :: Uri -> Int -> Int -> Int -> Int -> Expect
1975−mkL uri startLine startColumn endLine endColumn = ExpectLocation $ Location uri $ mkRange startLine startColumn endLine endColumn
1976−
1977−haddockTests :: TestTree
1978−haddockTests
1979− = testGroup "haddock"
1980− [ testCase "Num" $ checkHaddock
1981− (unlines
1982− [ "However, '(+)' and '(*)' are"
1983− , "customarily expected to define a ring and have the following properties:"
1984− , ""
1985− , "[__Associativity of (+)__]: @(x + y) + z@ = @x + (y + z)@"
1986− , "[__Commutativity of (+)__]: @x + y@ = @y + x@"
1987− , "[__@fromInteger 0@ is the additive identity__]: @x + fromInteger 0@ = @x@"
1988− ]
1989− )
1990− (unlines
1991− [ ""
1992− , ""
1993− , "However, `(+)` and `(*)` are"
1994− , "customarily expected to define a ring and have the following properties: "
1995− , "+ ****Associativity of (+)****: `(x + y) + z` = `x + (y + z)`"
1996− , "+ ****Commutativity of (+)****: `x + y` = `y + x`"
1997− , "+ ****`fromInteger 0` is the additive identity****: `x + fromInteger 0` = `x`"
1998− ]
1999− )
2000− , testCase "unsafePerformIO" $ checkHaddock
2001− (unlines
2002− [ "may require"
2003− , "different precautions:"
2004− , ""
2005− , " * Use @{\\-\\# NOINLINE foo \\#-\\}@ as a pragma on any function @foo@"
2006− , " that calls 'unsafePerformIO'. If the call is inlined,"
2007− , " the I\\/O may be performed more than once."
2008− , ""
2009− , " * Use the compiler flag @-fno-cse@ to prevent common sub-expression"
2010− , " elimination being performed on the module."
2011− , ""
2012− ]
2013− )
2014− (unlines
2015− [ ""
2016− , ""
2017− , "may require"
2018− , "different precautions: "
2019− , "+ Use `{-# NOINLINE foo #-}` as a pragma on any function `foo` "
2020− , " that calls `unsafePerformIO` . If the call is inlined,"
2021− , " the I/O may be performed more than once."
2022− , ""
2023− , "+ Use the compiler flag `-fno-cse` to prevent common sub-expression"
2024− , " elimination being performed on the module."
2025− , ""
2026− ]
2027− )
2028− ]
2029− where
2030− checkHaddock s txt = spanDocToMarkdownForTest s @?= txt
2031−
2032−cradleTests :: TestTree
2033−cradleTests = testGroup "cradle"
2034− [testGroup "dependencies" [sessionDepsArePickedUp]
2035− ,testGroup "loading" [loadCradleOnlyonce]
2036− ,testGroup "multi" [simpleMultiTest, simpleMultiTest2]
2037− ]
2038−
2039−loadCradleOnlyonce :: TestTree
2040−loadCradleOnlyonce = testGroup "load cradle only once"
2041− [ testSession' "implicit" implicit
2042− , testSession' "direct" direct
2043− ]
2044− where
2045− direct dir = do
2046− liftIO $ writeFileUTF8 (dir </> "hie.yaml")
2047− "cradle: {direct: {arguments: []}}"
2048− test dir
2049− implicit dir = test dir
2050− test _dir = do
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
2060−
2061−
2062−dependentFileTest :: TestTree
2063−dependentFileTest = testGroup "addDependentFile"
2064− [testGroup "file-changed" [testSession' "test" test]
2065− ]
2066− where
2067− test dir = do
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"
2075− , "foo :: Int"
2076− , "foo = 1 + $(do"
2077− , " qAddDependentFile \"dep-file.txt\""
2078− , " f <- qRunIO (readFile \"dep-file.txt\")"
2079− , " if f == \"B\" then [| 1 |] else lift f)"
2080− ]
2081− let bazContent = T.unlines ["module Baz where", "import Foo"]
2082− _ <-createDoc "Foo.hs" "haskell" fooContent
2083− doc <- createDoc "Baz.hs" "haskell" bazContent
2084− expectDiagnostics
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
2091− , _text = "f = ()"
2092− }
2093− -- Modifying Baz will now trigger Foo to be rebuilt as well
2094− changeDoc doc [change]
2095− expectDiagnostics [("Foo.hs", [])]
2096−
2097−
2098−cradleLoadedMessage :: Session FromServerMessage
2099−cradleLoadedMessage = satisfy $ \case
2100− NotCustomServer (NotificationMessage _ (CustomServerMethod m) _) -> m == cradleLoadedMethod
2101− _ -> False
2102−
2103−cradleLoadedMethod :: T.Text
2104−cradleLoadedMethod = "ghcide/cradle/loaded"
2105−
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
2109−withoutStackEnv s =
2110− bracket
2111− (mapM getEnv vars >>= \prevState -> mapM_ unsetEnv vars >> pure prevState)
2112− (\prevState -> mapM_ (\(var, value) -> restore var value) (zip vars prevState))
2113− (const s)
2114− where vars =
2115− [ "GHC_PACKAGE_PATH"
2116− , "GHC_ENVIRONMENT"
2117− , "HASKELL_DIST_DIR"
2118− , "HASKELL_PACKAGE_SANDBOX"
2119− , "HASKELL_PACKAGE_SANDBOXES"
2120− ]
2121− restore var Nothing = unsetEnv var
2122− restore var (Just val) = setEnv var val True
2123−
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
2138−
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
2155−
2156−sessionDepsArePickedUp :: TestTree
2157−sessionDepsArePickedUp = testSession'
2158− "session-deps-are-picked-up"
2159− $ \dir -> do
2160− liftIO $
2161− writeFileUTF8
2162− (dir </> "hie.yaml")
2163− "cradle: {direct: {arguments: []}}"
2164− -- Open without OverloadedStrings and expect an error.
2165− doc <- createDoc "Foo.hs" "haskell" fooContent
2166− expectDiagnostics
2167− [("Foo.hs", [(DsError, (3, 6), "Couldn't match expected type")])]
2168− -- Update hie.yaml to enable OverloadedStrings.
2169− liftIO $
2170− writeFileUTF8
2171− (dir </> "hie.yaml")
2172− "cradle: {direct: {arguments: [-XOverloadedStrings]}}"
2173− -- Send change event.
2174− let change =
2175− TextDocumentContentChangeEvent
2176− { _range = Just (Range (Position 4 0) (Position 4 0)),
2177− _rangeLength = Nothing,
2178− _text = "\n"
2179− }
2180− changeDoc doc [change]
2181− -- Now no errors.
2182− expectDiagnostics [("Foo.hs", [])]
2183− where
2184− fooContent =
2185− T.unlines
2186− [ "module Foo where",
2187− "import Data.Text",
2188− "foo :: Text",
2189− "foo = \"hello\""
2190− ]
2191−
2192−
21931571----------------------------------------------------------------------
21941572-- Utils
21951573
@@ -2197,12 +1575,6 @@ sessionDepsArePickedUp = testSession'
21971575testSession :: String -> Session () -> TestTree
21981576testSession name = testCase name . run
21991577
2200−testSessionWithExtraFiles :: FilePath -> String -> (FilePath -> Session ()) -> TestTree
2201−testSessionWithExtraFiles prefix name = testCase name . runWithExtraFiles prefix
2202−
2203−testSession' :: String -> (FilePath -> Session ()) -> TestTree
2204−testSession' name = testCase name . run'
2205−
22061578testSessionWait :: String -> Session () -> TestTree
22071579testSessionWait name = testSession name .
22081580 -- Check that any diagnostics produced were already consumed by the test case.
@@ -2213,53 +1585,24 @@ testSessionWait name = testSession name .
22131585 -- Experimentally, 0.5s seems to be long enough to wait for any final diagnostics to appear.
22141586 ( >> expectNoMoreDiagnostics 0.5)
22151587
2216−pickActionWithTitle :: T.Text -> [CAResult] -> IO CodeAction
2217−pickActionWithTitle title actions = do
2218− assertBool ("Found no matching actions: " <> show titles) (not $ null matches)
2219− return $ head matches
2220− where
2221− titles =
2222− [ actionTitle
2223− | CACodeAction CodeAction { _title = actionTitle } <- actions
2224− ]
2225− matches =
2226− [ action
2227− | CACodeAction action@CodeAction { _title = actionTitle } <- actions
2228− , title == actionTitle
2229− ]
1588+pickActionWithTitle :: T.Text -> [CAResult] -> CodeAction
1589+pickActionWithTitle title actions = head
1590+ [ action
1591+ | CACodeAction action@CodeAction{ _title = actionTitle } <- actions
1592+ , title == actionTitle ]
22301593
22311594mkRange :: Int -> Int -> Int -> Int -> Range
22321595mkRange a b c d = Range (Position a b) (Position c d)
22331596
22341597run :: Session a -> IO a
2235−run s = withTempDir $ \dir -> runInDir dir s
2236−
2237−runWithExtraFiles :: FilePath -> (FilePath -> Session a) -> IO a
2238−runWithExtraFiles prefix s = withTempDir $ \dir -> do
2239− copyTestDataFiles dir prefix
2240− runInDir dir (s dir)
2241−
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)
2249−
2250−run' :: (FilePath -> Session a) -> IO a
2251−run' s = withTempDir $ \dir -> runInDir dir (s dir)
2252−
2253−runInDir :: FilePath -> Session a -> IO a
2254−runInDir dir s = do
1598+run s = withTempDir $ \dir -> do
22551599 ghcideExe <- locateGhcideExecutable
22561600
22571601 -- Temporarily hack around https://github.com/mpickering/hie-bios/pull/56
22581602 -- since the package import test creates "Data/List.hs", which otherwise has no physical home
22591603 createDirectoryIfMissing True $ dir ++ "/Data"
22601604
2261−
2262− let cmd = unwords [ghcideExe, "--lsp", "--test", "--cwd", dir]
1605+ let cmd = unwords [ghcideExe, "--lsp", "--cwd", dir]
22631606 -- HIE calls getXgdDirectory which assumes that HOME is set.
22641607 -- Only sets HOME if it wasn't already set.
22651608 setEnv "HOME" "/homeless-shelter" False
@@ -2276,7 +1619,7 @@ runInDir dir s = do
22761619openTestDataDoc :: FilePath -> Session TextDocumentIdentifier
22771620openTestDataDoc path = do
22781621 source <- liftIO $ readFileUtf8 $ "test/data" </> path
2279− createDoc path "haskell" source
1622+ openDoc' path "haskell" source
22801623
22811624findCodeActions :: TextDocumentIdentifier -> Range -> [T.Text] -> Session [CodeAction]
22821625findCodeActions doc range expectedTitles = do
@@ -2291,7 +1634,7 @@ findCodeActions doc range expectedTitles = do
22911634 [ actionTitle
22921635 | CACodeAction CodeAction { _title = actionTitle } <- actions
22931636 ]
2294− ++ " is not a superset of "
1637+ ++ "is not a superset of "
22951638 ++ show expectedTitles
22961639 liftIO $ case matches of
22971640 Nothing -> assertFailure msg
@@ -2304,193 +1647,13 @@ findCodeAction doc range t = head <$> findCodeActions doc range [t]
23041647unitTests :: TestTree
23051648unitTests = do
23061649 testGroup "Unit"
2307− [ testCase "empty file path does NOT work with the empty String literal" $
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:"
2314− uriPath @?= ""
2315− , testCase "from empty path URI" $ do
2316− let uri = Uri "file://"
2317− uriToFilePath' uri @?= Just ""
1650+ [ testCase "empty file path" $
1651+ uriToFilePath' (fromNormalizedUri $ filePathToUri' "") @?= Just ""
23181652 ]
23191653
2320−positionMappingTests :: TestTree
2321−positionMappingTests =
2322− testGroup "position mapping"
2323− [ testGroup "toCurrent"
2324− [ testCase "before" $
2325− toCurrent
2326− (Range (Position 0 1) (Position 0 3))
2327− "ab"
2328− (Position 0 0) @?= Just (Position 0 0)
2329− , testCase "after, same line, same length" $
2330− toCurrent
2331− (Range (Position 0 1) (Position 0 3))
2332− "ab"
2333− (Position 0 3) @?= Just (Position 0 3)
2334− , testCase "after, same line, increased length" $
2335− toCurrent
2336− (Range (Position 0 1) (Position 0 3))
2337− "abc"
2338− (Position 0 3) @?= Just (Position 0 4)
2339− , testCase "after, same line, decreased length" $
2340− toCurrent
2341− (Range (Position 0 1) (Position 0 3))
2342− "a"
2343− (Position 0 3) @?= Just (Position 0 2)
2344− , testCase "after, next line, no newline" $
2345− toCurrent
2346− (Range (Position 0 1) (Position 0 3))
2347− "abc"
2348− (Position 1 3) @?= Just (Position 1 3)
2349− , testCase "after, next line, newline" $
2350− toCurrent
2351− (Range (Position 0 1) (Position 0 3))
2352− "abc\ndef"
2353− (Position 1 0) @?= Just (Position 2 0)
2354− , testCase "after, same line, newline" $
2355− toCurrent
2356− (Range (Position 0 1) (Position 0 3))
2357− "abc\nd"
2358− (Position 0 4) @?= Just (Position 1 2)
2359− , testCase "after, same line, newline + newline at end" $
2360− toCurrent
2361− (Range (Position 0 1) (Position 0 3))
2362− "abc\nd\n"
2363− (Position 0 4) @?= Just (Position 2 1)
2364− , testCase "after, same line, newline + newline at end" $
2365− toCurrent
2366− (Range (Position 0 1) (Position 0 1))
2367− "abc"
2368− (Position 0 1) @?= Just (Position 0 4)
2369− ]
2370− , testGroup "fromCurrent"
2371− [ testCase "before" $
2372− fromCurrent
2373− (Range (Position 0 1) (Position 0 3))
2374− "ab"
2375− (Position 0 0) @?= Just (Position 0 0)
2376− , testCase "after, same line, same length" $
2377− fromCurrent
2378− (Range (Position 0 1) (Position 0 3))
2379− "ab"
2380− (Position 0 3) @?= Just (Position 0 3)
2381− , testCase "after, same line, increased length" $
2382− fromCurrent
2383− (Range (Position 0 1) (Position 0 3))
2384− "abc"
2385− (Position 0 4) @?= Just (Position 0 3)
2386− , testCase "after, same line, decreased length" $
2387− fromCurrent
2388− (Range (Position 0 1) (Position 0 3))
2389− "a"
2390− (Position 0 2) @?= Just (Position 0 3)
2391− , testCase "after, next line, no newline" $
2392− fromCurrent
2393− (Range (Position 0 1) (Position 0 3))
2394− "abc"
2395− (Position 1 3) @?= Just (Position 1 3)
2396− , testCase "after, next line, newline" $
2397− fromCurrent
2398− (Range (Position 0 1) (Position 0 3))
2399− "abc\ndef"
2400− (Position 2 0) @?= Just (Position 1 0)
2401− , testCase "after, same line, newline" $
2402− fromCurrent
2403− (Range (Position 0 1) (Position 0 3))
2404− "abc\nd"
2405− (Position 1 2) @?= Just (Position 0 4)
2406− , testCase "after, same line, newline + newline at end" $
2407− fromCurrent
2408− (Range (Position 0 1) (Position 0 3))
2409− "abc\nd\n"
2410− (Position 2 1) @?= Just (Position 0 4)
2411− , testCase "after, same line, newline + newline at end" $
2412− fromCurrent
2413− (Range (Position 0 1) (Position 0 1))
2414− "abc"
2415− (Position 0 4) @?= Just (Position 0 1)
2416− ]
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.
2423− let gen = do
2424− rope <- genRope
2425− range <- genRange rope
2426− PrintableText replacement <- arbitrary
2427− oldPos <- genPosition rope
2428− pure (range, replacement, oldPos)
2429− forAll
2430− (suchThatMap gen
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
2435− let gen = do
2436− rope <- genRope
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)
2442− forAll
2443− (suchThatMap gen
2444− (\(range, replacement, newPos) -> (range, replacement, newPos,) <$> fromCurrent range replacement newPos)) $
2445− \(range, replacement, newPos, oldPos) ->
2446− toCurrent range replacement oldPos === Just newPos
2447− ]
2448− ]
2449−
2450−newtype PrintableText = PrintableText { getPrintableText :: T.Text }
2451− deriving Show
2452−
2453−instance Arbitrary PrintableText where
2454− arbitrary = PrintableText . T.pack . getPrintableString <$> arbitrary
2455−
2456−
2457−genRope :: Gen Rope
2458−genRope = Rope.fromText . getPrintableText <$> arbitrary
2459−
2460−genPosition :: Rope -> Gen Position
2461−genPosition r = do
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
2467−
2468−genRange :: Rope -> Gen Range
2469−genRange r = do
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)
2474− endColumn <-
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
2480−
2481−-- | Get the ith line of a rope, starting from 0. Trailing newline not included.
2482−nthLine :: Int -> Rope -> Rope
2483−nthLine i r
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
2488−
2489−getWatchedFilesSubscriptionsUntil :: forall end . (FromJSON end, Typeable end) => Session [Maybe Value]
2490−getWatchedFilesSubscriptionsUntil = do
2491− msgs <- manyTill (Just <$> message @RegisterCapabilityRequest <|> Nothing <$ anyMessage) (message @end)
2492− return
2493− [ args
2494− | Just RequestMessage{_params = RegistrationParams (List regs)} <- msgs
2495− , Registration _id WorkspaceDidChangeWatchedFiles args <- regs
2496− ]
1654+-- | Wrapper around 'LSPTest.openDoc'' that sends file creation events
1655+openDoc' :: FilePath -> String -> T.Text -> Session TextDocumentIdentifier
1656+openDoc' fp name contents = do
1657+ res@(TextDocumentIdentifier uri) <- LSPTest.openDoc' fp name contents
1658+ sendNotification WorkspaceDidChangeWatchedFiles (DidChangeWatchedFilesParams $ List [FileEvent uri FcCreated])
1659+ return res
test/src/Development/IDE/Test.hs+2−8
@@ -1,13 +1,10 @@
11-- Copyright (c) 2019 The DAML Authors. All rights reserved.
22-- SPDX-License-Identifier: Apache-2.0
33
4−{-# LANGUAGE DuplicateRecordFields #-}
5−
64module Development.IDE.Test
75 ( Cursor
86 , cursorPosition
97 , requireDiagnostic
10− , diagnostic
118 , expectDiagnostics
129 , expectNoMoreDiagnostics
1310 ) where
@@ -18,7 +15,7 @@ import Control.Monad
1815import Control.Monad.IO.Class
1916import qualified Data.Map.Strict as Map
2017import qualified Data.Text as T
21−import Language.Haskell.LSP.Test hiding (message)
18+import Language.Haskell.LSP.Test hiding (message, openDoc')
2219import qualified Language.Haskell.LSP.Test as LspTest
2320import Language.Haskell.LSP.Types
2421import Language.Haskell.LSP.Types.Lens as Lsp
@@ -80,7 +77,7 @@ expectDiagnostics expected = do
8077 go m
8178 | Map.null m = pure ()
8279 | otherwise = do
83− diagsNot <- skipManyTill anyMessage diagnostic
80+ diagsNot <- skipManyTill anyMessage LspTest.message :: Session PublishDiagnosticsNotification
8481 let fileUri = diagsNot ^. params . uri
8582 case Map.lookup (diagsNot ^. params . uri . to toNormalizedUri) m of
8683 Nothing -> do
@@ -99,9 +96,6 @@ expectDiagnostics expected = do
9996 " but got " <> show actual
10097 go $ Map.delete (diagsNot ^. params . uri . to toNormalizedUri) m
10198
102−diagnostic :: Session PublishDiagnosticsNotification
103−diagnostic = LspTest.message
104−
10599standardizeQuotes :: T.Text -> T.Text
106100standardizeQuotes msg = let
107101 repl '‘' = '\''