on master help sign in
.azure/linux-stack.ymlnew file+63
since splitsnapshots
unifiedsplit
@@ -0,0 +1,63 @@
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.ymlnew file+68
@@ -0,0 +1,68 @@
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'))
.editorconfignew file+11
@@ -0,0 +1,11 @@
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
.gitignorenew file+10
@@ -0,0 +1,10 @@
1+dist/
2+.stack-work/
3+dist-newstyle/
4+cabal.project.local
5+*~
6+*.lock
7+/.tasty-rerun-log
8+.vscode
9+/.hlint-*
10+bench/example
.hlint.yamlnew file+132
@@ -0,0 +1,132 @@
1+# HLint configuration file
2+# https://github.com/ndmitchell/hlint
3+##########################
4+
5+# To run HLint do:
6+# $ hlint --git -j4
7+
8+# Warnings currently triggered by our code
9+- ignore: {name: "Use <$>"}
10+- ignore: {name: "Use :"}
11+- ignore: {name: "Redundant do"}
12+- ignore: {name: "Avoid lambda"}
13+- ignore: {name: "Use newtype instead of data"}
14+- ignore: {name: "Use fromMaybe"}
15+- ignore: {name: "Use unless"}
16+- ignore: {name: "Move brackets to avoid $"}
17+- ignore: {name: "Eta reduce"}
18+- ignore: {name: "Parse error"}
19+- ignore: {name: "Reduce duplication"}
20+- ignore: {name: "Use ++"}
21+- ignore: {name: "Use $>"}
22+- ignore: {name: "Use section"}
23+- ignore: {name: "Use record patterns"}
24+- ignore: {name: "Use camelCase"}
25+- ignore: {name: "Use uncurry"}
26+- ignore: {name: "Avoid lambda using `infix`"}
27+
28+# Off by default hints we like
29+- warn: {name: Use module export list}
30+
31+# Condemn nub and friends
32+- warn: {lhs: nub (sort x), rhs: Data.List.Extra.nubSort x}
33+- warn: {lhs: nub, rhs: Data.List.Extra.nubOrd}
34+- warn: {lhs: nubBy, rhs: Data.List.Extra.nubOrdBy}
35+- warn: {lhs: Data.List.Extra.nubOn, rhs: Data.List.Extra.nubOrdOn}
36+
37+# DA specific hints
38+- warn: {lhs: Data.Text.pack (DA.Pretty.renderPlain x), rhs: DA.Pretty.renderPlain x}
39+- warn: {lhs: Data.Text.Extended.pack (DA.Pretty.renderPlain x), rhs: DA.Pretty.renderPlain x}
40+- warn: {lhs: DA.Pretty.renderPlain (DA.Pretty.pretty x), rhs: DA.Pretty.renderPretty x}
41+- warn: {lhs: Data.Text.readFile, rhs: Data.Text.Extended.readFileUtf8}
42+- warn: {lhs: Data.Text.writeFile, rhs: Data.Text.Extended.writeFileUtf8}
43+- warn: {lhs: Data.Text.Lazy.readFile, rhs: Data.Text.Extended.readFileUtf8}
44+- warn: {lhs: Data.Text.Lazy.writeFile, rhs: Data.Text.Extended.writeFileUtf8}
45+- warn: {lhs: System.Environment.setEnv, rhs: System.Environment.Blank.setEnv}
46+
47+# Specify additional command line arguments
48+#
49+- arguments: ["--cpp-include=include"]
50+
51+- extensions:
52+ - default: true
53+
54+ # Extensions enabled by `bazel` and `da-ghci` by default. We ban them here
55+ # to avoid useless pragmas piling up on the top of files.
56+ - {name: BangPatterns, within: []}
57+ - {name: DeriveDataTypeable, within: []}
58+ - {name: DeriveFoldable, within: []}
59+ - {name: DeriveFunctor, within: []}
60+ - {name: DeriveGeneric, within: []}
61+ - {name: DeriveTraversable, within: []}
62+ - {name: FlexibleContexts, within: []}
63+ - {name: GeneralizedNewtypeDeriving, within: []}
64+ - {name: LambdaCase, within: []}
65+ - {name: NamedFieldPuns, within: []}
66+ - {name: OverloadedStrings, within: []}
67+ - {name: PackageImports, within: []}
68+ - {name: RecordWildCards, within: []}
69+ - {name: ScopedTypeVariables, within: []}
70+ - {name: StandaloneDeriving, within: []}
71+ - {name: TupleSections, within: []}
72+ - {name: TypeApplications, within: []}
73+ - {name: ViewPatterns, within: []}
74+
75+ # Shady extensions
76+ - name: CPP
77+ within:
78+ - Development.IDE.Compat
79+ - Development.IDE.Core.FileStore
80+ - Development.IDE.Core.Compile
81+ - Development.IDE.Core.Rules
82+ - Development.IDE.GHC.Compat
83+ - Development.IDE.GHC.Orphans
84+ - Development.IDE.GHC.Util
85+ - Development.IDE.Import.FindImports
86+ - Development.IDE.LSP.Outline
87+ - Development.IDE.Spans.Calculate
88+ - Development.IDE.Spans.Documentation
89+ - Development.IDE.Spans.Common
90+ - Development.IDE.Plugin.CodeAction
91+ - Development.IDE.Plugin.Completions
92+ - Development.IDE.Plugin.Completions.Logic
93+ - Main
94+
95+- flags:
96+ - default: false
97+ - {name: [-Wno-missing-signatures, -Wno-orphans, -Wno-overlapping-patterns, -Wno-incomplete-patterns, -Wno-missing-fields, -Wno-unused-matches]}
98+ - {name: [-Wno-dodgy-imports], within: Main}
99+# - modules:
100+# - {name: [Data.Set, Data.HashSet], as: Set} # if you import Data.Set qualified, it must be as 'Set'
101+# - {name: Control.Arrow, within: []} # Certain modules are banned entirely
102+#
103+- functions:
104+ # Things that are unsafe in Haskell base library
105+ - {name: unsafeInterleaveIO, within: []}
106+ - {name: unsafeDupablePerformIO, within: []}
107+ - {name: unsafeCoerce, within: []}
108+ # Things that are a bit dangerous in the GHC API
109+ - {name: nameModule, within: []}
110+
111+# Add custom hints for this project
112+#
113+# Will suggest replacing "wibbleMany [myvar]" with "wibbleOne myvar"
114+# - error: {lhs: "wibbleMany [x]", rhs: wibbleOne x}
115+
116+# Turn on hints that are off by default
117+#
118+# Ban "module X(module X) where", to require a real export list
119+# - warn: {name: Use explicit module export list}
120+#
121+# Replace a $ b $ c with a . b $ c
122+# - group: {name: dollar, enabled: true}
123+#
124+# Generalise map to fmap, ++ to <>
125+# - group: {name: generalise, enabled: true}
126+
127+# Ignore some builtin hints
128+# - ignore: {name: Use let}
129+# - ignore: {name: Use const, within: SpecialModule} # Only within certain modules
130+
131+# Define some custom infix operators
132+# - fixity: infixr 3 ~^#^~
BUILD.bazeldeleted−91
@@ -1,91 +0,0 @@
1−# Copyright (c) 2019 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
2−# SPDX-License-Identifier: Apache-2.0
3−
4−load("//bazel_tools:haskell.bzl", "da_haskell_binary", "da_haskell_library")
5−load("@os_info//:os_info.bzl", "is_windows")
6−
7−depends = [
8− "aeson",
9− "async",
10− "base",
11− "binary",
12− "bytestring",
13− "containers",
14− "data-default",
15− "deepseq",
16− "directory",
17− "extra",
18− "filepath",
19− "hashable",
20− "haskell-lsp",
21− "haskell-lsp-types",
22− "mtl",
23− "network-uri",
24− "prettyprinter",
25− "prettyprinter-ansi-terminal",
26− "rope-utf16-splay",
27− "sorted-list",
28− "shake",
29− "stm",
30− "syb",
31− "text",
32− "time",
33− "transformers",
34− "unordered-containers",
35− "utf8-string",
36−]
37−
38−da_haskell_library(
39− name = "hie-core",
40− srcs = glob(["src/**/*.hs"]),
41− hazel_deps = depends + [
42− "ghc-lib",
43− "ghc-lib-parser",
44− ],
45− src_strip_prefix = "src",
46− visibility = ["//visibility:public"],
47−)
48−
49−da_haskell_library(
50− name = "hie-core-public",
51− srcs = glob(["src/**/*.hs"]),
52− compiler_flags = ["-DGHC_STABLE"],
53− hazel_deps = depends + [
54− "ghc",
55− "ghc-boot",
56− "ghc-boot-th",
57− ],
58− # Override the -hide-package flags defined in WORKSPACE
59− # -hide-package=ghc-boot-th -hide-package=ghc-boot
60− repl_ghci_args = [
61− "-package=ghc-boot",
62− "-package=ghc-boot-th",
63− ],
64− src_strip_prefix = "src",
65− visibility = ["//visibility:public"],
66−)
67−
68−da_haskell_binary(
69− name = "hie-core-exe",
70− srcs = glob(["exe/**/*.hs"]),
71− hazel_deps = [
72− "base",
73− "containers",
74− "data-default",
75− "directory",
76− "extra",
77− "filepath",
78− "ghc-paths",
79− "ghc",
80− "haskell-lsp",
81− "hie-bios",
82− "optparse-applicative",
83− "shake",
84− "text",
85− ],
86− src_strip_prefix = "test",
87− visibility = ["//visibility:public"],
88− deps = [
89− "hie-core-public",
90− ],
91−) if not is_windows else None # Disable on Windows until ghc-paths is fixed upstream
CHANGELOG.mdnew file+85
@@ -0,0 +1,85 @@
1+### unreleased
2+
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+
38+### 0.0.6 (2020-01-10)
39+
40+* Fix type in hover information for do-notation and list
41+ comprehensions (see #243).
42+* Fix hover and goto-definition for multi-clause definitions (see #252).
43+* Upgrade to `hie-bios-0.3` (see #257)
44+* Upgrade to `haskell-lsp-0.19` (see #254)
45+* Code lenses for missing signatures are displayed even if the warning
46+ has not been enabled. The warning itself will not be shown if it is
47+ not enabled. (see #232)
48+* Define `__GHCIDE__` when running CPP to allow for `ghcide`-specific
49+ workarounds. (see #264)
50+* Fix some filepath normalization issues. (see #266)
51+* Fix build with `shake-0.18.4` (see #272)
52+* Fix hover for type constructors and type classes. (see #267)
53+* Support custom preprocessors (see #282)
54+* Add support for code completions (see #227)
55+* Code action for removing redundant symbols from imports (see #290)
56+* Support document symbol requests (see #293)
57+* Show CPP errors as diagnostics (see #296)
58+* Code action for adding suggested imports (see #295)
59+
60+### 0.0.5 (2019-12-12)
61+
62+* Support for GHC plugins (see #192)
63+* Update to haskell-lsp 0.18 (see #203)
64+* Initial support for `TemplateHaskell` (see #222)
65+* Code lenses for missing signatures. These are only shown if
66+ `-Wmissing-signatures` is enabled. (see #224)
67+* Fix path normalisation on Windows (see #225)
68+* Fix flickering of the progress indicator (see #230)
69+
70+### 0.0.4 (2019-10-20)
71+
72+* Add a ``--version`` cli option (thanks @jacg)
73+* Update to use progress reporting as defined in LSP 3.15. The VSCode
74+ extension has also been updated and should now be making use of
75+ this.
76+* Properly declare that we should support code actions. This helps
77+ with some clients that rely on this information to enable code
78+ actions (thanks @jacg).
79+* Fix a race condition caused by sharing the finder cache between
80+ concurrent compilations.
81+* Avoid normalizing include dirs. This avoids issues where the same
82+ file ends up twice in the module graph, e.g., with different casing
83+ for drive letters.
84+
85+### 0.0.3 (2019-09-21)
LICENSEnew file+201
@@ -0,0 +1,201 @@
1+ Apache License
2+ Version 2.0, January 2004
3+ http://www.apache.org/licenses/
4+
5+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6+
7+ 1. Definitions.
8+
9+ "License" shall mean the terms and conditions for use, reproduction,
10+ and distribution as defined by Sections 1 through 9 of this document.
11+
12+ "Licensor" shall mean the copyright owner or entity authorized by
13+ the copyright owner that is granting the License.
14+
15+ "Legal Entity" shall mean the union of the acting entity and all
16+ other entities that control, are controlled by, or are under common
17+ control with that entity. For the purposes of this definition,
18+ "control" means (i) the power, direct or indirect, to cause the
19+ direction or management of such entity, whether by contract or
20+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21+ outstanding shares, or (iii) beneficial ownership of such entity.
22+
23+ "You" (or "Your") shall mean an individual or Legal Entity
24+ exercising permissions granted by this License.
25+
26+ "Source" form shall mean the preferred form for making modifications,
27+ including but not limited to software source code, documentation
28+ source, and configuration files.
29+
30+ "Object" form shall mean any form resulting from mechanical
31+ transformation or translation of a Source form, including but
32+ not limited to compiled object code, generated documentation,
33+ and conversions to other media types.
34+
35+ "Work" shall mean the work of authorship, whether in Source or
36+ Object form, made available under the License, as indicated by a
37+ copyright notice that is included in or attached to the work
38+ (an example is provided in the Appendix below).
39+
40+ "Derivative Works" shall mean any work, whether in Source or Object
41+ form, that is based on (or derived from) the Work and for which the
42+ editorial revisions, annotations, elaborations, or other modifications
43+ represent, as a whole, an original work of authorship. For the purposes
44+ of this License, Derivative Works shall not include works that remain
45+ separable from, or merely link (or bind by name) to the interfaces of,
46+ the Work and Derivative Works thereof.
47+
48+ "Contribution" shall mean any work of authorship, including
49+ the original version of the Work and any modifications or additions
50+ to that Work or Derivative Works thereof, that is intentionally
51+ submitted to Licensor for inclusion in the Work by the copyright owner
52+ or by an individual or Legal Entity authorized to submit on behalf of
53+ the copyright owner. For the purposes of this definition, "submitted"
54+ means any form of electronic, verbal, or written communication sent
55+ to the Licensor or its representatives, including but not limited to
56+ communication on electronic mailing lists, source code control systems,
57+ and issue tracking systems that are managed by, or on behalf of, the
58+ Licensor for the purpose of discussing and improving the Work, but
59+ excluding communication that is conspicuously marked or otherwise
60+ designated in writing by the copyright owner as "Not a Contribution."
61+
62+ "Contributor" shall mean Licensor and any individual or Legal Entity
63+ on behalf of whom a Contribution has been received by Licensor and
64+ subsequently incorporated within the Work.
65+
66+ 2. Grant of Copyright License. Subject to the terms and conditions of
67+ this License, each Contributor hereby grants to You a perpetual,
68+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69+ copyright license to reproduce, prepare Derivative Works of,
70+ publicly display, publicly perform, sublicense, and distribute the
71+ Work and such Derivative Works in Source or Object form.
72+
73+ 3. Grant of Patent License. Subject to the terms and conditions of
74+ this License, each Contributor hereby grants to You a perpetual,
75+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76+ (except as stated in this section) patent license to make, have made,
77+ use, offer to sell, sell, import, and otherwise transfer the Work,
78+ where such license applies only to those patent claims licensable
79+ by such Contributor that are necessarily infringed by their
80+ Contribution(s) alone or by combination of their Contribution(s)
81+ with the Work to which such Contribution(s) was submitted. If You
82+ institute patent litigation against any entity (including a
83+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84+ or a Contribution incorporated within the Work constitutes direct
85+ or contributory patent infringement, then any patent licenses
86+ granted to You under this License for that Work shall terminate
87+ as of the date such litigation is filed.
88+
89+ 4. Redistribution. You may reproduce and distribute copies of the
90+ Work or Derivative Works thereof in any medium, with or without
91+ modifications, and in Source or Object form, provided that You
92+ meet the following conditions:
93+
94+ (a) You must give any other recipients of the Work or
95+ Derivative Works a copy of this License; and
96+
97+ (b) You must cause any modified files to carry prominent notices
98+ stating that You changed the files; and
99+
100+ (c) You must retain, in the Source form of any Derivative Works
101+ that You distribute, all copyright, patent, trademark, and
102+ attribution notices from the Source form of the Work,
103+ excluding those notices that do not pertain to any part of
104+ the Derivative Works; and
105+
106+ (d) If the Work includes a "NOTICE" text file as part of its
107+ distribution, then any Derivative Works that You distribute must
108+ include a readable copy of the attribution notices contained
109+ within such NOTICE file, excluding those notices that do not
110+ pertain to any part of the Derivative Works, in at least one
111+ of the following places: within a NOTICE text file distributed
112+ as part of the Derivative Works; within the Source form or
113+ documentation, if provided along with the Derivative Works; or,
114+ within a display generated by the Derivative Works, if and
115+ wherever such third-party notices normally appear. The contents
116+ of the NOTICE file are for informational purposes only and
117+ do not modify the License. You may add Your own attribution
118+ notices within Derivative Works that You distribute, alongside
119+ or as an addendum to the NOTICE text from the Work, provided
120+ that such additional attribution notices cannot be construed
121+ as modifying the License.
122+
123+ You may add Your own copyright statement to Your modifications and
124+ may provide additional or different license terms and conditions
125+ for use, reproduction, or distribution of Your modifications, or
126+ for any such Derivative Works as a whole, provided Your use,
127+ reproduction, and distribution of the Work otherwise complies with
128+ the conditions stated in this License.
129+
130+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131+ any Contribution intentionally submitted for inclusion in the Work
132+ by You to the Licensor shall be under the terms and conditions of
133+ this License, without any additional terms or conditions.
134+ Notwithstanding the above, nothing herein shall supersede or modify
135+ the terms of any separate license agreement you may have executed
136+ with Licensor regarding such Contributions.
137+
138+ 6. Trademarks. This License does not grant permission to use the trade
139+ names, trademarks, service marks, or product names of the Licensor,
140+ except as required for reasonable and customary use in describing the
141+ origin of the Work and reproducing the content of the NOTICE file.
142+
143+ 7. Disclaimer of Warranty. Unless required by applicable law or
144+ agreed to in writing, Licensor provides the Work (and each
145+ Contributor provides its Contributions) on an "AS IS" BASIS,
146+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147+ implied, including, without limitation, any warranties or conditions
148+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149+ PARTICULAR PURPOSE. You are solely responsible for determining the
150+ appropriateness of using or redistributing the Work and assume any
151+ risks associated with Your exercise of permissions under this License.
152+
153+ 8. Limitation of Liability. In no event and under no legal theory,
154+ whether in tort (including negligence), contract, or otherwise,
155+ unless required by applicable law (such as deliberate and grossly
156+ negligent acts) or agreed to in writing, shall any Contributor be
157+ liable to You for damages, including any direct, indirect, special,
158+ incidental, or consequential damages of any character arising as a
159+ result of this License or out of the use or inability to use the
160+ Work (including but not limited to damages for loss of goodwill,
161+ work stoppage, computer failure or malfunction, or any and all
162+ other commercial damages or losses), even if such Contributor
163+ has been advised of the possibility of such damages.
164+
165+ 9. Accepting Warranty or Additional Liability. While redistributing
166+ the Work or Derivative Works thereof, You may choose to offer,
167+ and charge a fee for, acceptance of support, warranty, indemnity,
168+ or other liability obligations and/or rights consistent with this
169+ License. However, in accepting such obligations, You may act only
170+ on Your own behalf and on Your sole responsibility, not on behalf
171+ of any other Contributor, and only if You agree to indemnify,
172+ defend, and hold each Contributor harmless for any liability
173+ incurred by, or claims asserted against, such Contributor by reason
174+ of your accepting any such warranty or additional liability.
175+
176+ END OF TERMS AND CONDITIONS
177+
178+ APPENDIX: How to apply the Apache License to your work.
179+
180+ To apply the Apache License to your work, attach the following
181+ boilerplate notice, with the fields enclosed by brackets "[]"
182+ replaced with your own identifying information. (Don't include
183+ the brackets!) The text should be enclosed in the appropriate
184+ comment syntax for the file format. We also recommend that a
185+ file or class name and description of purpose be included on the
186+ same "printed page" as the copyright notice for easier
187+ identification within third-party archives.
188+
189+ Copyright 2019 Digital Asset (Switzerland) GmbH and/or its affiliates
190+
191+ Licensed under the Apache License, Version 2.0 (the "License");
192+ you may not use this file except in compliance with the License.
193+ You may obtain a copy of the License at
194+
195+ http://www.apache.org/licenses/LICENSE-2.0
196+
197+ Unless required by applicable law or agreed to in writing, software
198+ distributed under the License is distributed on an "AS IS" BASIS,
199+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200+ See the License for the specific language governing permissions and
201+ limitations under the License.
README.md+284−65
@@ -1,115 +1,334 @@
1−# `hie-core` (Haskell IDE Core)
1+# `ghcide` - A library for building Haskell IDE tooling
2+
3+Note: `ghcide` was previously called `hie-core`.
2
4
3Our vision is that you should build an IDE by combining:
5Our vision is that you should build an IDE by combining:
4
6
7+![vscode](https://raw.githubusercontent.com/digital-asset/ghcide/master/img/vscode2.png)
8+
5* [`hie-bios`](https://github.com/mpickering/hie-bios) for determining where your files are, what are their dependencies, what extensions are enabled and so on;
9* [`hie-bios`](https://github.com/mpickering/hie-bios) for determining where your files are, what are their dependencies, what extensions are enabled and so on;
6−* `hie-core` (i.e. this library) for defining how to type check, when to type check, and producing diagnostic messages;
10+* `ghcide` (i.e. this library) for defining how to type check, when to type check, and producing diagnostic messages;
7* A bunch of plugins that haven't yet been written, e.g. [`hie-hlint`](https://github.com/ndmitchell/hlint) and [`hie-ormolu`](https://github.com/tweag/ormolu), to choose which features you want;
11* A bunch of plugins that haven't yet been written, e.g. [`hie-hlint`](https://github.com/ndmitchell/hlint) and [`hie-ormolu`](https://github.com/tweag/ormolu), to choose which features you want;
8* [`haskell-lsp`](https://github.com/alanz/haskell-lsp) for sending those messages to a [Language Server Protocol (LSP)](https://microsoft.github.io/language-server-protocol/) server;
12* [`haskell-lsp`](https://github.com/alanz/haskell-lsp) for sending those messages to a [Language Server Protocol (LSP)](https://microsoft.github.io/language-server-protocol/) server;
9* An extension for your editor. We provide a [VS Code extension](https://code.visualstudio.com/api) as `extension` in this directory, although the components work in other LSP editors too (see below for instructions using Emacs).
13* An extension for your editor. We provide a [VS Code extension](https://code.visualstudio.com/api) as `extension` in this directory, although the components work in other LSP editors too (see below for instructions using Emacs).
10
14
11There are more details about our approach [in this blog post](https://4ta.uk/p/shaking-up-the-ide).
15There are more details about our approach [in this blog post](https://4ta.uk/p/shaking-up-the-ide).
12
16
17+## Features
18+
19+`ghcide` already exports the following features via the lsp protocol:
20+
21+| Feature | LSP name |
22+| - | - |
23+| Display error messages (parse errors, typecheck errors, etc.) and enabled warnings. | diagnostics |
24+| Go to definition in local package | definition |
25+| Display type and source module of values | hover |
26+| Remove redundant imports, replace suggested typos for values and module imports, fill type holes, insert missing type signatures, add suggested ghc extensions | codeAction (quickfix) |
27+
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+
13## Using it
45## Using it
14
46
15−### Install `hie-core`
47+### Install `ghcide`
16
48
17−First install the `hie-core` binary using `stack` or `cabal`, e.g.
49+#### With Nix
18
50
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.
62+
63+#### With Cabal or Stack
64+
65+First install the `ghcide` binary using `stack` or `cabal`, e.g.
66+
19−1. `git clone https://github.com/digital-asset/daml.git`
67+1. `git clone https://github.com/digital-asset/ghcide.git`
20−2. `cd daml/compiler/hie-core`
68+2. `cd ghcide`
213. `cabal install` or `stack install` (and make sure `~/.local/bin` is on your `$PATH`)
693. `cabal install` or `stack install` (and make sure `~/.local/bin` is on your `$PATH`)
22
70
23−It's important that `hie-core` is compiled with the same compiler you use to build your projects.
71+It's important that `ghcide` is compiled with the same compiler you use to build your projects.
24
72
25−### Test `hie-core`
73+### Test `ghcide`
26
74
27−Next, check that `hie-core` is capable of loading your code. Change to the project directory and run `hie-core`, which will try and load everything using the same code as the IDE, but in a way that's much easier to understand. For example, taking the example of [`shake`](https://github.com/ndmitchell/shake), running `hie-core` gives some error messages and warnings before reporting at the end:
75+Next, check that `ghcide` is capable of loading your code. Change to the project directory and run `ghcide`, which will try and load everything using the same code as the IDE, but in a way that's much easier to understand. For example, taking the example of [`shake`](https://github.com/ndmitchell/shake), running `ghcide` gives some error messages and warnings before reporting at the end:
28
76
29−```
77+```console
30−Files that worked: 152
31−Files that failed: 6
78+Files that failed:
32 * .\model\Main.hs
79 * .\model\Main.hs
33 * .\model\Model.hs
80 * .\model\Model.hs
34 * .\model\Test.hs
81 * .\model\Test.hs
35 * .\model\Util.hs
82 * .\model\Util.hs
36 * .\output\docs\Main.hs
83 * .\output\docs\Main.hs
37 * .\output\docs\Part_Architecture_md.hs
84 * .\output\docs\Part_Architecture_md.hs
38−Done
85+Completed (152 worked, 6 failed)
39```
86```
40
87
41Of the 158 files in Shake, as of this moment, 152 can be loaded by the IDE, but 6 can't (error messages for the reasons they can't be loaded are given earlier). The failing files are all prototype work or test output, meaning I can confidently use Shake.
88Of the 158 files in Shake, as of this moment, 152 can be loaded by the IDE, but 6 can't (error messages for the reasons they can't be loaded are given earlier). The failing files are all prototype work or test output, meaning I can confidently use Shake.
42
89
43−The `hie-core` executable mostly relies on [`hie-bios`](https://github.com/mpickering/hie-bios) to do the difficult work of setting up your GHC environment. If it doesn't work, see [the `hie-bios` manual](https://github.com/mpickering/hie-bios#readme) to get it working. My default fallback is to figure it out by hand and create a `direct` style [`hie.yaml`](https://github.com/ndmitchell/shake/blob/master/hie.yaml) listing the command line arguments to load the project.
90+The `ghcide` executable mostly relies on [`hie-bios`](https://github.com/mpickering/hie-bios) to do the difficult work of setting up your GHC environment. If it doesn't work, see [the `hie-bios` manual](https://github.com/mpickering/hie-bios#readme) to get it working. My default fallback is to figure it out by hand and create a `direct` style [`hie.yaml`](https://github.com/ndmitchell/shake/blob/master/hie.yaml) listing the command line arguments to load the project.
91+
92+If 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.
93+
94+### Optimal project setup
44
95
45−Once you have got `hie-core` working outside the editor, the next step is to pick which editor to integrate with.
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.
46
97
47### Using with VS Code
98### Using with VS Code
48
99
49−Install the VS code extension
100+You can install the VSCode extension from the [VSCode
101+marketplace](https://marketplace.visualstudio.com/items?itemName=DigitalAssetHoldingsLLC.ghcide).
50
102
51−1. `cd compiler/hie-core/extension`
103+### Using with Atom
52−2. `npm ci`
104+
53−3. `npm install vsce --global` (may require `sudo`)
105+You can follow the [instructions](https://github.com/moodmosaic/ide-haskell-ghcide#readme) to install with `apm`.
54−4. `vsce package`
55−5. `code --install-extension hie-core-0.0.1.vsix`
56
106
57−Now openning a `.hs` file should work with `hie-core`.
107+### Using with Sublime Text
108+
109+* Install [LSP](https://packagecontrol.io/packages/LSP)
110+* Press Ctrl+Shift+P or Cmd+Shift+P in Sublime Text and search for *Preferences: LSP Settings*, then paste these settings
111+```
112+{
113+ "clients":
114+ {
115+ "ghcide":
116+ {
117+ "enabled" : true,
118+ "languageId": "haskell",
119+ "command" : ["ghcide", "--lsp"],
120+ "scopes" : ["source.haskell"],
121+ "syntaxes" : ["Packages/Haskell/Haskell.sublime-syntax"]
122+ }
123+ }
124+}
125+```
58
126
59### Using with Emacs
127### Using with Emacs
60
128
61−The frst step is to install required Emacs packages. If you don't already have [Melpa](https://melpa.org/#/) package installation configured in your `.emacs`, put this stanza at the top.
129+If you don't already have [MELPA](https://melpa.org/#/) package installation configured, visit MELPA [getting started](https://melpa.org/#/getting-started) page to get set up. Then, install [`use-package`](https://melpa.org/#/use-package).
130+
131+Now you have a choice of two different Emacs packages which can be used to communicate with the `ghcide` LSP server:
132+
133++ `lsp-ui`
134++ `eglot` (requires Emacs 26.1+)
135+
136+In each case, you can enable support by adding the shown lines to your `.emacs`:
137+
138+#### lsp-ui
62
139
63```elisp
140```elisp
64−;;Melpa packages support
141+;; LSP
65−(require 'package)
142+(use-package flycheck
66−(let* ((no-ssl (and (memq system-type '(windows-nt ms-dos))
143+ :ensure t
67− (not (gnutls-available-p))))
144+ :init
68− (proto (if no-ssl "http" "https")))
145+ (global-flycheck-mode t))
69− (when no-ssl
146+(use-package yasnippet
70− (warn "\
147+ :ensure t)
71−Your version of Emacs does not support SSL connections,
148+(use-package lsp-mode
72−which is unsafe because it allows man-in-the-middle attacks.
149+ :ensure t
73−There are two things you can do about this warning:
150+ :hook (haskell-mode . lsp)
74−1. Install an Emacs version that does support SSL and be safe.
151+ :commands lsp)
75−2. Remove this warning from your init file so you won't see it again."))
152+(use-package lsp-ui
76− ;; Comment/uncomment these two lines to enable/disable MELPA and MELPA Stable as desired
153+ :ensure t
77− (add-to-list 'package-archives (cons "melpa" (concat proto "://melpa.org/packages/")) t)
154+ :commands lsp-ui-mode)
78− ;;(add-to-list 'package-archives (cons "melpa-stable" (concat proto "://stable.melpa.org/packages/")) t)
155+(use-package lsp-haskell
79− (when (< emacs-major-version 24)
156+ :ensure t
80− ;; For important compatibility libraries like cl-lib
157+ :config
81− (add-to-list 'package-archives (cons "gnu" (concat proto "://elpa.gnu.org/packages/")))))
158+ (setq lsp-haskell-process-path-hie "ghcide")
82−(package-initialize)
159+ (setq lsp-haskell-process-args-hie '())
83−;; Remember : to avoid package-not-found errors, refresh the package
160+ ;; Comment/uncomment this line to see interactions between lsp client/server.
84−;; database now and then with M-x package-refresh-contents.
161+ ;;(setq lsp-log-io t)
162+)
85```
163```
86
164
87−When this is in your `.emacs` and evaluated, `M-x package-refresh-contents` to get the package database downloaded and then `M-x package-list-packages` to display the available packages. Click on a package to install it. You'll need to install the following packages:
165+#### eglot
88
166
89−* `lsp-haskell`
167+````elisp
90−* `lsp-ui`
168+(use-package eglot
91−* `flycheck`
169+ :ensure t
92−* `yasnippet`
170+ :config
171+ (add-to-list 'eglot-server-programs '(haskell-mode . ("ghcide" "--lsp"))))
172+````
93
173
94−When done with this, add the following lines to your `.emacs`:
174+### Using with Vim/Neovim
95
175
96−```elisp
176+#### LanguageClient-neovim
97−;; LSP support for Haskell
177+Install [LanguageClient-neovim](https://github.com/autozimu/LanguageClient-neovim)
98−(require 'lsp)
178+
99−(require 'lsp-haskell)
179+Add this to your vim config:
100−(require 'yasnippet)
180+```vim
101−(add-hook 'haskell-mode-hook #'lsp)
181+let g:LanguageClient_rootMarkers = ['*.cabal', 'stack.yaml']
102−(setq lsp-haskell-process-path-hie "hie-core")
182+let g:LanguageClient_serverCommands = {
103−(setq lsp-haskell-process-args-hie '())
183+ \ 'rust': ['rls'],
184+ \ 'haskell': ['ghcide', '--lsp'],
185+ \ }
104```
186```
105
187
106−Optionally, you may wish to add the following conveniences:
188+Refer to `:he LanguageClient` for more details on usage and configuration.
107
189
108−```elisp
190+#### vim-lsp
109−;; Enable LSP logging (helpful for debugging)
191+Install [vim-lsp](https://github.com/prabirshrestha/vim-lsp).
110−(setq lsp-log-io t)
192+
193+Add this to your vim config:
194+
195+```vim
196+au User lsp_setup call lsp#register_server({
197+ \ 'name': 'ghcide',
198+ \ 'cmd': {server_info->['/your/path/to/ghcide', '--lsp']},
199+ \ 'whitelist': ['haskell'],
200+ \ })
201+```
202+
203+To verify it works move your cursor over a symbol and run `:LspHover`.
111
204
112−;; Keyboard mappings for goto next/previous error
205+### coc.nvim
113−(define-key flymake-mode-map (kbd "M-n") 'flymake-goto-next-error)
206+
114−(define-key flymake-mode-map (kbd "M-p") 'flymake-goto-prev-error)
207+Install [coc.nvim](https://github.com/neoclide/coc.nvim)
208+
209+Add this to your coc-settings.json (which you can edit with :CocConfig):
210+
211+```json
212+{
213+ "languageserver": {
214+ "haskell": {
215+ "command": "ghcide",
216+ "args": [
217+ "--lsp"
218+ ],
219+ "rootPatterns": [
220+ ".stack.yaml",
221+ ".hie-bios",
222+ "BUILD.bazel",
223+ "cabal.config",
224+ "package.yaml"
225+ ],
226+ "filetypes": [
227+ "hs",
228+ "lhs",
229+ "haskell"
230+ ]
231+ }
232+ }
233+}
234+```
235+
236+Here'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/).
242+
243+### SpaceVim
244+
245+In the `autocomplete` layer, add the `autocomplete_method` option to force the use of `coc`:
246+
247+```toml
248+[[layers]]
249+ name = 'autocomplete'
250+ auto-completion-return-key-behavior = "complete"
251+ auto-completion-tab-key-behavior = "smart"
252+ [options]
253+ autocomplete_method = "coc"
115```
254```
255+
256+Add this to your coc-settings.json (which you can edit with :CocConfig):
257+
258+```json
259+{
260+ "languageserver": {
261+ "haskell": {
262+ "command": "ghcide",
263+ "args": [
264+ "--lsp"
265+ ],
266+ "rootPatterns": [
267+ ".stack.yaml",
268+ ".hie-bios",
269+ "BUILD.bazel",
270+ "cabal.config",
271+ "package.yaml"
272+ ],
273+ "filetypes": [
274+ "hs",
275+ "lhs",
276+ "haskell"
277+ ]
278+ }
279+ }
280+}
281+```
282+
283+This example above describes a setup in which `ghcide` is installed
284+using `stack install ghcide` within a project.
285+
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+
300+## Hacking on ghcide
301+
302+To build and work on `ghcide` itself, you can use Stack or cabal, e.g.,
303+running `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.
312+
313+### Building the extension
314+
315+For development, you can also the VSCode extension from this repository (see
316+https://code.visualstudio.com/docs/setup/mac for details on adding
317+`code` to your `$PATH`):
318+
319+1. `cd extension/`
320+2. `npm ci`
321+3. `npm run vscepackage`
322+4. `code --install-extension ghcide-0.0.1.vsix`
323+
324+Now opening a `.hs` file should work with `ghcide`.
325+
326+## History and relationship to other Haskell IDE's
327+
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+
330+The 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).
331+
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.
333+
334+The 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.ymlnew file+18
@@ -0,0 +1,18 @@
1+# Build master commits
2+trigger:
3+ batch: false
4+ branches:
5+ include:
6+ - master
7+ - azure*
8+
9+# Enable PR triggers that target the master branch
10+pr:
11+ autoCancel: true # cancel previous builds on push
12+ branches:
13+ include:
14+ - master
15+
16+jobs:
17+ - template: ./.azure/linux-stack.yml
18+ - template: ./.azure/windows-stack.yml
bench/Main.hsnew file+287
@@ -0,0 +1,287 @@
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+--------------------------------------------------------------------------------------------
cbits/getmodtime.cnew file+21
@@ -0,0 +1,21 @@
1+// Copyright (c) 2019 The DAML Authors. All rights reserved.
2+// SPDX-License-Identifier: Apache-2.0
3+
4+#include <sys/stat.h>
5+#include <time.h>
6+int getmodtime(const char* pathname, time_t* sec, long* nsec) {
7+ struct stat s;
8+ int r = stat(pathname, &s);
9+ if (r != 0) {
10+ return r;
11+ }
12+#ifdef __APPLE__
13+ *sec = s.st_mtimespec.tv_sec;
14+ *nsec = s.st_mtimespec.tv_nsec;
15+#else
16+ *sec = s.st_mtim.tv_sec;
17+ *nsec = s.st_mtim.tv_nsec;
18+#endif
19+ return 0;
20+}
21+
docs/Setup.mdnew file+145
@@ -0,0 +1,145 @@
1+# Setup Troubleshooting
2+
3+This page serves as a dumping ground for setup problems and their resolutions. We recommend everyone first runs `ghcide` on the console to check what files in their project load, and only the moves on to using `ghcide` through an editor (e.g. VS Code).
4+
5+## "mismatched interface file versions"
6+
7+If you see a problem such as:
8+
9+```console
10+File: ./test/Spec.hs
11+Range: 1:0-1:0
12+Source: typecheck
13+Severity: DsError
14+Message:
15+ test/Spec.hs:1:1: error:
16+ Bad interface file:
17+ /Users/daml/.stack/programs/x86_64-osx/ghc-8.6.4/lib/ghc-8.6.4/base-4.12.0.0/Prelude.hi
18+ mismatched interface file versions (wanted "8065", got "8064")
19+```
20+
21+The cause is that your program is configured to use a different GHC to the one you built `ghcide` with. In `ghcide` you can view the version number it was compiled with on the first line as:
22+
23+```console
24+ghcide version: 0.0.3 (GHC: 8.6.5)
25+```
26+
27+You can see the version of GHC being used by this project in the second-last line of the output with `ghc-8.6.4/`, or in in mismatch interfaces of wanted `8065` (aka 8.6.5), got `8064` (aka 8.6.4). The solution is to use the same GHC version in both places.
28+
29+## “failed to load interface for ‘…’ There are files missing”
30+
31+If you see a problem such as:
32+
33+```console
34+File: ./src/File/FileStream.hs
35+Range: 1:0-100001:0
36+Source: typecheck
37+Severity: DsError
38+Message:
39+ Program error: Failed to load interface for ‘Data.DList’
40+Files that failed:
41+ There are files missing in the ‘dlist-0.8.0.7’ package,
42+ * ./src/File/FileStream.hs
43+ try running 'ghc-pkg check'.
44+ Use -v to see a list of the files searched for.
45+```
46+
47+It might be caused by `ghcide` picking up the wrong cradle. In
48+particular, this has been observed when running in a `nix-shell` where
49+`ghcide` picked up the default cradle. Try setting the cradle
50+explicitly, e.g., to use the cabal cradle create a `hie.yaml` file
51+with the following content:
52+
53+```
54+cradle: {cabal: {component: "mylibrary"}}
55+```
56+
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+
68+## ghc: readCreateProcess: does not exist
69+
70+On Linux: try `stack exec ghcide`` instead of `ghcide` directly.
71+
72+I was getting this in Windows: `ghcide.exe: ghc: readCreateProcess: does not exist (No such file or directory)`
73+
74+And we figured a hack around for this:
75+
76+VSCode user or workspace settings, add these:
77+
78+ "hic.executablePath": "stack",
79+ "hic.arguments": "exec ghcide -- --lsp"
80+
81+Since I use stack. Required if you don't have a `ghc` on your path.
82+
83+## Could not find module ...
84+
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"}}
100+
101+## Works in `ghcide` but not my editor
102+
103+Does `ghcide` alone work on the console? Did you first enter a Nix shell? Or run `stack exec ghcide`? If so, there are two options:
104+
105+1. Run your editor via the same mechanism, e.g. `stack exec code`.
106+2. Change the extension to use the executable as `stack` and the arguments as `exec -- ghcide --lsp`.
107+
108+## Issues with Nix
109+
110+If you are using packages installed by Nix, then often Nix will set `NIX_GHC_LIBDIR` to say where the libraries are installed. `ghcide` can cope with that. However, sometimes the `ghc` on your shell will actually be a shell script that sets `NIX_GHC_LIBDIR`, which `ghcide` can't find. If that happens, you need to either set `NIX_GHC_LIBDIR` (so `ghcide` can see it) or use a proper [Nix compatible wrapper](https://github.com/hercules-ci/ghcide-nix) over `ghcide`.
111+
112+## ghcide: this operation requires -fexternal-interpreter
113+
114+This can happen if you have a GHC compiled without GHC library support. This seems to be [the case](https://github.com/input-output-hk/haskell.nix/issues/313) with `haskell.nix` at the moment.
115+
116+## Symbol’s value as variable is void: capability
117+
118+As described [here](https://github.com/emacs-lsp/lsp-mode/issues/770#issuecomment-483540119) and [here](https://github.com/emacs-lsp/lsp-mode/issues/517#issuecomment-445448700), the default installation of `lsp-mode`, `lsp-ui`, `lsp-ui-mode` and `lsp-haskell` as described in [ghcide's "Using with Emacs" section](https://github.com/digital-asset/ghcide/#using-with-emacs) may result in the following error message:
119+
120+```
121+Symbol’s value as variable is void: capability
122+```
123+
124+This can be caused by either an old version of the Emacs package `dash`, or a cached `.elc` file for an old version. A fix consists of (re)moving the old package from ~/.emacs.d/elpa/ and installing it again, e.g. via M-x `package-list-packages` RET and M-x `package-install` RET `dash` RET. If this is not enough,
125+
126+```
127+find ~/.emacs.d -name '*.elc' -exec rm {} \;
128+```
129+
130+(which causes recompilation of all bytecode-compiled scripts.)
131+
132+
133+## Docker stack builds
134+
135+You'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+10−2
@@ -1,4 +1,4 @@
1−-- Copyright (c) 2019 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
1+-- Copyright (c) 2019 The DAML Authors. All rights reserved.
2-- SPDX-License-Identifier: Apache-2.0
2-- SPDX-License-Identifier: Apache-2.0
3
3
4module Arguments(Arguments(..), getArguments) where
4module Arguments(Arguments(..), getArguments) where
@@ -10,6 +10,10 @@ data Arguments = Arguments
10 {argLSP :: Bool
10 {argLSP :: Bool
11 ,argsCwd :: Maybe FilePath
11 ,argsCwd :: Maybe FilePath
12 ,argFiles :: [FilePath]
12 ,argFiles :: [FilePath]
13+ ,argsVersion :: Bool
14+ ,argsShakeProfiling :: Maybe FilePath
15+ ,argsTesting :: Bool
16+ ,argsThreads :: Int
13 }
17 }
14
18
15getArguments :: IO Arguments
19getArguments :: IO Arguments
@@ -18,10 +22,14 @@ getArguments = execParser opts
18 opts = info (arguments <**> helper)
22 opts = info (arguments <**> helper)
19 ( fullDesc
23 ( fullDesc
20 <> progDesc "Used as a test bed to check your IDE will work"
24 <> progDesc "Used as a test bed to check your IDE will work"
21− <> header "hie-core - the core of a Haskell IDE")
25+ <> header "ghcide - the core of a Haskell IDE")
22
26
23arguments :: Parser Arguments
27arguments :: Parser Arguments
24arguments = Arguments
28arguments = Arguments
25 <$> switch (long "lsp" <> help "Start talking to an LSP server")
29 <$> switch (long "lsp" <> help "Start talking to an LSP server")
26 <*> optional (strOption $ long "cwd" <> metavar "DIR" <> help "Change to this directory")
30 <*> optional (strOption $ long "cwd" <> metavar "DIR" <> help "Change to this directory")
27 <*> many (argument str (metavar "FILES/DIRS..."))
31 <*> many (argument str (metavar "FILES/DIRS..."))
32+ <*> switch (long "version" <> help "Show ghcide and GHC versions")
33+ <*> 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+610−54
@@ -1,16 +1,33 @@
1−-- Copyright (c) 2019 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
1+-- Copyright (c) 2019 The DAML Authors. All rights reserved.
2-- SPDX-License-Identifier: Apache-2.0
2-- SPDX-License-Identifier: Apache-2.0
3+{-# OPTIONS_GHC -Wno-dodgy-imports #-} -- GHC no longer exports def in GHC 8.6 and above
4+{-# LANGUAGE CPP #-} -- To get precise GHC version
5+{-# LANGUAGE TemplateHaskell #-}
6+{-# LANGUAGE TypeFamilies #-}
3
7
4module Main(main) where
8module Main(main) where
5
9
10+import Linker (initDynLinker)
11+import Data.IORef
12+import NameCache
13+import Packages
14+import Module
6import Arguments
15import Arguments
7−import Data.Maybe
16+import Control.Concurrent.Async
8−import Data.List.Extra
9−import System.FilePath
10import Control.Concurrent.Extra
17import Control.Concurrent.Extra
18+import Control.Exception
11import Control.Monad.Extra
19import Control.Monad.Extra
20+import Control.Monad.IO.Class
12import Data.Default
21import Data.Default
13−import System.Time.Extra
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
14import Development.IDE.Core.FileStore
31import Development.IDE.Core.FileStore
15import Development.IDE.Core.OfInterest
32import Development.IDE.Core.OfInterest
16import Development.IDE.Core.Service
33import Development.IDE.Core.Service
@@ -22,79 +39,131 @@ import Development.IDE.Types.Location
22import Development.IDE.Types.Diagnostics
39import Development.IDE.Types.Diagnostics
23import Development.IDE.Types.Options
40import Development.IDE.Types.Options
24import Development.IDE.Types.Logger
41import Development.IDE.Types.Logger
25−import qualified Data.Text.IO as T
42+import 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
26import Language.Haskell.LSP.Messages
47import Language.Haskell.LSP.Messages
48+import Language.Haskell.LSP.Types
49+import Data.Aeson (ToJSON(toJSON))
27import Development.IDE.LSP.LanguageServer
50import Development.IDE.LSP.LanguageServer
28−import System.Directory.Extra as IO
51+import qualified System.Directory.Extra as IO
29import System.Environment
52import System.Environment
30import System.IO
53import System.IO
54+import System.Exit
55+import System.FilePath
56+import System.Directory
57+import System.Time.Extra
58+import HIE.Bios.Environment (addCmdOpts, makeDynFlagsAbsolute)
59+import Paths_ghcide
60+import Development.GitRev
31−import Development.Shake hiding (Env)
61+import Development.Shake (Action, action)
32−import qualified Data.Set as Set
62+import qualified Data.HashSet as HashSet
63+import qualified Data.HashMap.Strict as HM
64+import 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)
71+import GHC hiding (def)
72+import GHC.Check ( VersionCheck(..), makeGhcVersionChecker )
73+import Data.Either.Extra
33
74
34−-- import CmdLineParser
75+import HIE.Bios.Cradle
35−-- import DynFlags
76+import HIE.Bios.Types
36−-- import Panic
37−import GHC
38−import qualified GHC.Paths
39
77
40−import HIE.Bios
78+import Utils
41
79
42−-- Set the GHC libdir to the nix libdir if it's present.
80+ghcideVersion :: IO String
43−getLibdir :: IO FilePath
81+ghcideVersion = do
44−getLibdir = fromMaybe GHC.Paths.libdir <$> lookupEnv "NIX_GHC_LIBDIR"
82+ path <- getExecutablePath
83+ let gitHashSection = case $(gitHash) of
84+ x | x == "UNKNOWN" -> ""
85+ x -> " (GIT hash: " <> x <> ")"
86+ return $ "ghcide version: " <> showVersion version
87+ <> " (GHC: " <> VERSION_ghc
88+ <> ") (PATH: " <> path <> ")"
89+ <> gitHashSection
45
90
46main :: IO ()
91main :: IO ()
47main = do
92main = do
48 -- WARNING: If you write to stdout before runLanguageServer
93 -- WARNING: If you write to stdout before runLanguageServer
49 -- then the language server will not work
94 -- then the language server will not work
50− hPutStrLn stderr "Starting hie-core"
51 Arguments{..} <- getArguments
95 Arguments{..} <- getArguments
52
96
97+ if argsVersion then ghcideVersion >>= putStrLn >> exitSuccess
98+ else hPutStrLn stderr {- see WARNING above -} =<< ghcideVersion
99+
53 -- lock to avoid overlapping output on stdout
100 -- lock to avoid overlapping output on stdout
54 lock <- newLock
101 lock <- newLock
55− let logger = makeOneLogger $ withLock lock . T.putStrLn
102+ let logger p = Logger $ \pri msg -> when (pri >= p) $ withLock lock $
103+ T.putStrLn $ T.pack ("[" ++ upper (show pri) ++ "] ") <> msg
56
104
57− whenJust argsCwd setCurrentDirectory
105+ whenJust argsCwd IO.setCurrentDirectory
58
106
59− dir <- getCurrentDirectory
107+ dir <- IO.getCurrentDirectory
60− hPutStrLn stderr dir
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+ }
61
116
62 if argLSP then do
117 if argLSP then do
63 t <- offsetTime
118 t <- offsetTime
64 hPutStrLn stderr "Starting LSP server..."
119 hPutStrLn stderr "Starting LSP server..."
65− runLanguageServer def def $ \event vfs -> do
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
66 t <- t
122 t <- t
67 hPutStrLn stderr $ "Started LSP server in " ++ showDuration t
123 hPutStrLn stderr $ "Started LSP server in " ++ showDuration t
68− let options = defaultIdeOptions $ liftIO $ newSession' =<< findCradle (dir <> "/")
124+ let options = (defaultIdeOptions $ loadSessionShake dir)
125+ { optReportProgress = clientSupportsProgress caps
126+ , optShakeProfiling = argsShakeProfiling
127+ , optTesting = argsTesting
128+ , optThreads = argsThreads
129+ }
130+ debouncer <- newAsyncDebouncer
69− initialise (mainRule >> action kick) event logger options vfs
131+ initialise caps (mainRule >> pluginRules plugins >> action kick)
132+ getLspId event (logger minBound) debouncer options vfs
70 else do
133 else do
71− putStrLn "[1/6] Finding hie-bios cradle"
134+ -- GHC produces messages with UTF8 in them, so make sure the terminal doesn't error
72− cradle <- findCradle (dir <> "/")
135+ hSetEncoding stdout utf8
73− print cradle
136+ hSetEncoding stderr utf8
74−
75− putStrLn "\n[2/6] Converting Cradle to GHC session"
76− env <- newSession' cradle
77
137
78− putStrLn "\n[3/6] Initialising IDE session"
138+ putStrLn $ "Ghcide setup tester in " ++ dir ++ "."
79− vfs <- makeVFSHandle
139+ putStrLn "Report bugs at https://github.com/digital-asset/ghcide/issues"
80− ide <- initialise mainRule (showEvent lock) logger (defaultIdeOptions $ return env) vfs
81
140
82− putStrLn "\n[4/6] Finding interesting files"
141+ putStrLn $ "\nStep 1/4: Finding files to test in " ++ dir
83− files <- nubOrd <$> expandFiles (argFiles ++ ["." | null argFiles])
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
84 putStrLn $ "Found " ++ show (length files) ++ " files"
145 putStrLn $ "Found " ++ show (length files) ++ " files"
85
146
86− putStrLn "\n[5/6] Setting interesting files"
147+ putStrLn "\nStep 2/4: Looking for hie.yaml files that control setup"
87− setFilesOfInterest ide $ Set.fromList $ map toNormalizedFilePath files
148+ cradles <- mapM findCradle files
149+ let ucradles = nubOrd cradles
150+ let n = length ucradles
151+ putStrLn $ "Found " ++ show n ++ " cradle" ++ ['s' | n /= 1]
152+ putStrLn "\nStep 3/4: Initializing the IDE"
153+ vfs <- makeVFSHandle
154+ debouncer <- newAsyncDebouncer
155+ ide <- initialise def mainRule (pure $ IdInt 0) (showEvent lock) (logger Info) debouncer (defaultIdeOptions $ loadSessionShake dir) vfs
88
156
89− putStrLn "\n[6/6] Loading interesting files"
157+ putStrLn "\nStep 4/4: Type checking the files"
158+ setFilesOfInterest ide $ HashSet.fromList $ map toNormalizedFilePath' files
90− results <- runActionSync ide $ uses TypeCheck $ map toNormalizedFilePath files
159+ results <- runActionSync ide $ uses TypeCheck (map toNormalizedFilePath' files)
91 let (worked, failed) = partition fst $ zip (map isJust results) files
160 let (worked, failed) = partition fst $ zip (map isJust results) files
92− putStrLn $ "Files that worked: " ++ show (length worked)
161+ when (failed /= []) $
93− putStrLn $ "Files that failed: " ++ show (length failed)
94− putStr $ unlines $ map ((++) " * " . snd) failed
162+ putStr $ unlines $ "Files that failed:" : map ((++) " * " . snd) failed
95−
96− putStrLn "Done"
97
163
164+ let files xs = let n = length xs in if n == 1 then "1 file" else show n ++ " files"
165+ putStrLn $ "\nCompleted (" ++ files worked ++ " worked, " ++ files failed ++ " failed)"
166+ return ()
98
167
99expandFiles :: [FilePath] -> IO [FilePath]
168expandFiles :: [FilePath] -> IO [FilePath]
100expandFiles = concatMapM $ \x -> do
169expandFiles = concatMapM $ \x -> do
@@ -103,7 +172,7 @@ expandFiles = concatMapM $ \x -> do
103 let recurse "." = True
172 let recurse "." = True
104 recurse x | "." `isPrefixOf` takeFileName x = False -- skip .git etc
173 recurse x | "." `isPrefixOf` takeFileName x = False -- skip .git etc
105 recurse x = takeFileName x `notElem` ["dist","dist-newstyle"] -- cabal directories
174 recurse x = takeFileName x `notElem` ["dist","dist-newstyle"] -- cabal directories
106− files <- filter (\x -> takeExtension x `elem` [".hs",".lhs"]) <$> listFilesInside (return . recurse) x
175+ files <- filter (\x -> takeExtension x `elem` [".hs",".lhs"]) <$> IO.listFilesInside (return . recurse) x
107 when (null files) $
176 when (null files) $
108 fail $ "Couldn't find any .hs/.lhs files inside directory: " ++ x
177 fail $ "Couldn't find any .hs/.lhs files inside directory: " ++ x
109 return files
178 return files
@@ -112,16 +181,503 @@ expandFiles = concatMapM $ \x -> do
112kick :: Action ()
181kick :: Action ()
113kick = do
182kick = do
114 files <- getFilesOfInterest
183 files <- getFilesOfInterest
115− void $ uses TypeCheck $ Set.toList files
184+ void $ uses TypeCheck $ HashSet.toList files
116
185
117-- | Print an LSP event.
186-- | Print an LSP event.
118showEvent :: Lock -> FromServerMessage -> IO ()
187showEvent :: Lock -> FromServerMessage -> IO ()
119showEvent _ (EventFileDiagnostics _ []) = return ()
188showEvent _ (EventFileDiagnostics _ []) = return ()
120−showEvent lock (EventFileDiagnostics (toNormalizedFilePath -> file) diags) =
189+showEvent lock (EventFileDiagnostics (toNormalizedFilePath' -> file) diags) =
121− withLock lock $ T.putStrLn $ showDiagnosticsColored $ map (file,) diags
190+ withLock lock $ T.putStrLn $ showDiagnosticsColored $ map (file,ShowDiag,) diags
122showEvent lock e = withLock lock $ print e
191showEvent lock e = withLock lock $ print e
123
192
124−newSession' :: Cradle -> IO HscEnv
193+
125−newSession' cradle = getLibdir >>= \libdir -> runGhc (Just libdir) $ do
194+-- | Run the specific cradle on a specific FilePath via hie-bios.
126− initializeFlagsWithCradle "" cradle
195+cradleToSessionOpts :: Cradle a -> FilePath -> IO (Either [CradleError] ComponentOptions)
127− getSession
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
208+ libdir <- getLibdir
209+ env <- runGhc (Just libdir) getSession
210+ 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
386+
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)
403+
404+
405+
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+
592+-- | Memoize an IO function, with the characteristics:
593+--
594+-- * If multiple people ask for a result simultaneously, make sure you only compute it once.
595+--
596+-- * If there are exceptions, repeatedly reraise them.
597+--
598+-- * If the caller is aborted (async exception) finish computing it anyway.
599+memoIO :: Ord a => (a -> IO b) -> IO (a -> IO b)
600+memoIO op = do
601+ ref <- newVar Map.empty
602+ return $ \k -> join $ mask_ $ modifyVar ref $ \mp ->
603+ case Map.lookup k mp of
604+ Nothing -> do
605+ res <- onceFork $ op k
606+ return (Map.insert k res mp, res)
607+ 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.hsnew file+9
@@ -0,0 +1,9 @@
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"
extension/README.mdmode 100644 → 120000+1−3
@@ -1,3 +0,0 @@
1−A very simple haskell ide core frontend. More or less a bare bones LSP interface.
2−
3−To get it working run `npm install`, `code .` then press F5 to run.
@@ -0,0 +1 @@
1+../README.md
extension/package-lock.json+419−612
@@ -1,61 +1,46 @@
1{
1{
2− "name": "hie-core",
2+ "name": "ghcide",
3− "version": "0.0.1",
3+ "version": "0.0.2",
4 "lockfileVersion": 1,
4 "lockfileVersion": 1,
5 "requires": true,
5 "requires": true,
6 "dependencies": {
6 "dependencies": {
7 "@babel/code-frame": {
7 "@babel/code-frame": {
8− "version": "7.0.0",
8+ "version": "7.5.5",
9− "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz",
9+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz",
10− "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==",
10+ "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==",
11 "dev": true,
11 "dev": true,
12 "requires": {
12 "requires": {
13− "@babel/highlight": "7.0.0"
13+ "@babel/highlight": "^7.0.0"
14 }
14 }
15 },
15 },
16 "@babel/highlight": {
16 "@babel/highlight": {
17− "version": "7.0.0",
17+ "version": "7.5.0",
18− "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz",
18+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.5.0.tgz",
19− "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==",
19+ "integrity": "sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==",
20 "dev": true,
20 "dev": true,
21 "requires": {
21 "requires": {
22− "chalk": "2.4.2",
22+ "chalk": "^2.0.0",
23− "esutils": "2.0.2",
23+ "esutils": "^2.0.2",
24− "js-tokens": "4.0.0"
24+ "js-tokens": "^4.0.0"
25 }
25 }
26 },
26 },
27 "@types/mocha": {
27 "@types/mocha": {
28− "version": "2.2.48",
28+ "version": "5.2.7",
29− "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-2.2.48.tgz",
29+ "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.7.tgz",
30− "integrity": "sha512-nlK/iyETgafGli8Zh9zJVCTicvU3iajSkRwOh3Hhiva598CMqNJ4NcVCGMTGKpGpTYj/9R8RLzS9NAykSSCqGw==",
30+ "integrity": "sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==",
31 "dev": true
31 "dev": true
32 },
32 },
33 "@types/node": {
33 "@types/node": {
34− "version": "10.14.6",
34+ "version": "12.7.11",
35− "resolved": "https://registry.npmjs.org/@types/node/-/node-10.14.6.tgz",
35+ "resolved": "https://registry.npmjs.org/@types/node/-/node-12.7.11.tgz",
36− "integrity": "sha512-Fvm24+u85lGmV4hT5G++aht2C5I4Z4dYlWZIh62FAfFO/TfzXtPpoLI6I7AuBWkIFqZCnhFOoTT7RjjaIL5Fjg==",
36+ "integrity": "sha512-Otxmr2rrZLKRYIybtdG/sgeO+tHY20GxeDjcGmUnmmlCWyEnv2a2x1ZXBo3BTec4OiTXMQCiazB8NMBf0iRlFw==",
37 "dev": true
37 "dev": true
38 },
38 },
39− "agent-base": {
39+ "@types/vscode": {
40− "version": "4.2.1",
41− "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz",
42− "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==",
43− "dev": true,
44− "requires": {
45− "es6-promisify": "5.0.0"
46− }
47− },
48− "ajv": {
49− "version": "6.10.0",
40+ "version": "1.35.0",
50− "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz",
41+ "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.35.0.tgz",
51− "integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==",
42+ "integrity": "sha512-Iyliuu8Hv4qy4TEaevQzChh9UsTEcuaKdcHXBbvJnoJSF5Td2yNENOrPK+vuOaXJJBhQZb4BNJKOxt6caaQR8A==",
52− "dev": true,
43+ "dev": true
53− "requires": {
54− "fast-deep-equal": "2.0.1",
55− "fast-json-stable-stringify": "2.0.0",
56− "json-schema-traverse": "0.4.1",
57− "uri-js": "4.2.2"
58− }
59 },
44 },
60 "ansi-styles": {
45 "ansi-styles": {
61 "version": "3.2.1",
46 "version": "3.2.1",
@@ -63,7 +48,7 @@
63 "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
48 "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
64 "dev": true,
49 "dev": true,
65 "requires": {
50 "requires": {
66− "color-convert": "1.9.3"
51+ "color-convert": "^1.9.0"
67 }
52 }
68 },
53 },
69 "argparse": {
54 "argparse": {
@@ -72,56 +57,32 @@
72 "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
57 "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
73 "dev": true,
58 "dev": true,
74 "requires": {
59 "requires": {
75− "sprintf-js": "1.0.3"
60+ "sprintf-js": "~1.0.2"
76 }
61 }
77 },
62 },
78− "asn1": {
63+ "azure-devops-node-api": {
79− "version": "0.2.4",
64+ "version": "7.2.0",
80− "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
65+ "resolved": "https://registry.npmjs.org/azure-devops-node-api/-/azure-devops-node-api-7.2.0.tgz",
81− "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
66+ "integrity": "sha512-pMfGJ6gAQ7LRKTHgiRF+8iaUUeGAI0c8puLaqHLc7B8AR7W6GJLozK9RFeUHFjEGybC9/EB3r67WPd7e46zQ8w==",
82 "dev": true,
67 "dev": true,
83 "requires": {
68 "requires": {
84− "safer-buffer": "2.1.2"
69+ "os": "0.1.1",
70+ "tunnel": "0.0.4",
71+ "typed-rest-client": "1.2.0",
72+ "underscore": "1.8.3"
85 }
73 }
86 },
74 },
87− "assert-plus": {
88− "version": "1.0.0",
89− "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
90− "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
91− "dev": true
92− },
93− "asynckit": {
94− "version": "0.4.0",
95− "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
96− "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
97− "dev": true
98− },
99− "aws-sign2": {
100− "version": "0.7.0",
101− "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
102− "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=",
103− "dev": true
104− },
105− "aws4": {
106− "version": "1.8.0",
107− "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz",
108− "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==",
109− "dev": true
110− },
111 "balanced-match": {
75 "balanced-match": {
112 "version": "1.0.0",
76 "version": "1.0.0",
113 "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
77 "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
114 "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
78 "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
115 "dev": true
79 "dev": true
116 },
80 },
117− "bcrypt-pbkdf": {
81+ "boolbase": {
118− "version": "1.0.2",
82+ "version": "1.0.0",
119− "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
83+ "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
120− "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
84+ "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=",
121− "dev": true,
85+ "dev": true
122− "requires": {
123− "tweetnacl": "0.14.5"
124− }
125 },
86 },
126 "brace-expansion": {
87 "brace-expansion": {
127 "version": "1.1.11",
88 "version": "1.1.11",
@@ -129,20 +90,14 @@
129 "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
90 "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
130 "dev": true,
91 "dev": true,
131 "requires": {
92 "requires": {
132− "balanced-match": "1.0.0",
93+ "balanced-match": "^1.0.0",
133 "concat-map": "0.0.1"
94 "concat-map": "0.0.1"
134 }
95 }
135 },
96 },
136− "browser-stdout": {
97+ "buffer-crc32": {
137− "version": "1.3.0",
98+ "version": "0.2.13",
138− "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz",
99+ "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
139− "integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=",
100+ "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=",
140− "dev": true
141− },
142− "buffer-from": {
143− "version": "1.1.1",
144− "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
145− "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
146 "dev": true
101 "dev": true
147 },
102 },
148 "builtin-modules": {
103 "builtin-modules": {
@@ -151,21 +106,29 @@
151 "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=",
106 "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=",
152 "dev": true
107 "dev": true
153 },
108 },
154− "caseless": {
155− "version": "0.12.0",
156− "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
157− "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=",
158− "dev": true
159− },
160 "chalk": {
109 "chalk": {
161 "version": "2.4.2",
110 "version": "2.4.2",
162 "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
111 "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
163 "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
112 "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
164 "dev": true,
113 "dev": true,
165 "requires": {
114 "requires": {
166− "ansi-styles": "3.2.1",
115+ "ansi-styles": "^3.2.1",
167− "escape-string-regexp": "1.0.5",
116+ "escape-string-regexp": "^1.0.5",
168− "supports-color": "5.5.0"
117+ "supports-color": "^5.3.0"
118+ }
119+ },
120+ "cheerio": {
121+ "version": "1.0.0-rc.3",
122+ "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.3.tgz",
123+ "integrity": "sha512-0td5ijfUPuubwLUu0OBoe98gZj8C/AA+RW3v67GPlGOrvxWjZmBXiBCRU+I8VEiNyJzjth40POfHiz2RB3gImA==",
124+ "dev": true,
125+ "requires": {
126+ "css-select": "~1.2.0",
127+ "dom-serializer": "~0.1.1",
128+ "entities": "~1.1.1",
129+ "htmlparser2": "^3.9.1",
130+ "lodash": "^4.15.0",
131+ "parse5": "^3.0.1"
169 }
132 }
170 },
133 },
171 "color-convert": {
134 "color-convert": {
@@ -183,19 +146,10 @@
183 "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
146 "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
184 "dev": true
147 "dev": true
185 },
148 },
186− "combined-stream": {
187− "version": "1.0.8",
188− "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
189− "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
190− "dev": true,
191− "requires": {
192− "delayed-stream": "1.0.0"
193− }
194− },
195 "commander": {
149 "commander": {
196− "version": "2.20.0",
150+ "version": "2.20.1",
197− "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz",
151+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.1.tgz",
198− "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==",
152+ "integrity": "sha512-cCuLsMhJeWQ/ZpsFTbE765kvVfoeSddc4nU3up4fV+fDBcfUXnbITJ+JzhkdjzOqhURjZgujxaioam4RM9yGUg==",
199 "dev": true
153 "dev": true
200 },
154 },
201 "concat-map": {
155 "concat-map": {
@@ -204,67 +158,83 @@
204 "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
158 "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
205 "dev": true
159 "dev": true
206 },
160 },
207− "core-util-is": {
161+ "css-select": {
208− "version": "1.0.2",
162+ "version": "1.2.0",
209− "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
163+ "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz",
210− "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
164+ "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=",
211− "dev": true
212− },
213− "dashdash": {
214− "version": "1.14.1",
215− "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
216− "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
217 "dev": true,
165 "dev": true,
218 "requires": {
166 "requires": {
219− "assert-plus": "1.0.0"
167+ "boolbase": "~1.0.0",
168+ "css-what": "2.1",
169+ "domutils": "1.5.1",
170+ "nth-check": "~1.0.1"
220 }
171 }
221 },
172 },
222− "debug": {
173+ "css-what": {
223− "version": "3.1.0",
174+ "version": "2.1.3",
224− "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
175+ "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz",
225− "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
176+ "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==",
226− "dev": true,
177+ "dev": true
227− "requires": {
228− "ms": "2.0.0"
229− }
230 },
178 },
231− "delayed-stream": {
179+ "denodeify": {
180+ "version": "1.2.1",
181+ "resolved": "https://registry.npmjs.org/denodeify/-/denodeify-1.2.1.tgz",
182+ "integrity": "sha1-OjYof1A05pnnV3kBBSwubJQlFjE=",
183+ "dev": true
184+ },
185+ "didyoumean": {
232− "version": "1.0.0",
186+ "version": "1.2.1",
233− "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
187+ "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.1.tgz",
234− "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
188+ "integrity": "sha1-6S7f2tplN9SE1zwBcv0eugxJdv8=",
235 "dev": true
189 "dev": true
236 },
190 },
237 "diff": {
191 "diff": {
238− "version": "3.5.0",
192+ "version": "4.0.1",
239− "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz",
193+ "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.1.tgz",
240− "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==",
194+ "integrity": "sha512-s2+XdvhPCOF01LRQBC8hf4vhbVmI2CGS5aZnxLJlT5FtdhPCDFq80q++zK2KlrVorVDdL5BOGZ/VfLrVtYNF+Q==",
241 "dev": true
195 "dev": true
242 },
196 },
243− "ecc-jsbn": {
197+ "dom-serializer": {
244− "version": "0.1.2",
198+ "version": "0.1.1",
245− "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
199+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz",
246− "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
200+ "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==",
247 "dev": true,
201 "dev": true,
248 "requires": {
202 "requires": {
249− "jsbn": "0.1.1",
203+ "domelementtype": "^1.3.0",
250− "safer-buffer": "2.1.2"
204+ "entities": "^1.1.1"
251 }
205 }
252 },
206 },
253− "es6-promise": {
207+ "domelementtype": {
254− "version": "4.2.8",
208+ "version": "1.3.1",
255− "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz",
209+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz",
256− "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==",
210+ "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==",
257 "dev": true
211 "dev": true
258 },
212 },
259− "es6-promisify": {
213+ "domhandler": {
260− "version": "5.0.0",
214+ "version": "2.4.2",
261− "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz",
215+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz",
262− "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=",
216+ "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==",
263 "dev": true,
217 "dev": true,
264 "requires": {
218 "requires": {
265− "es6-promise": "4.2.8"
219+ "domelementtype": "1"
266 }
220 }
267 },
221 },
222+ "domutils": {
223+ "version": "1.5.1",
224+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz",
225+ "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=",
226+ "dev": true,
227+ "requires": {
228+ "dom-serializer": "0",
229+ "domelementtype": "1"
230+ }
231+ },
232+ "entities": {
233+ "version": "1.1.2",
234+ "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz",
235+ "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==",
236+ "dev": true
237+ },
268 "escape-string-regexp": {
238 "escape-string-regexp": {
269 "version": "1.0.5",
239 "version": "1.0.5",
270 "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
240 "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
@@ -278,50 +248,18 @@
278 "dev": true
248 "dev": true
279 },
249 },
280 "esutils": {
250 "esutils": {
281− "version": "2.0.2",
251+ "version": "2.0.3",
282− "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
252+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
283− "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=",
253+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
284− "dev": true
285− },
286− "extend": {
287− "version": "3.0.2",
288− "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
289− "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
290− "dev": true
291− },
292− "extsprintf": {
293− "version": "1.3.0",
294− "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
295− "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=",
296− "dev": true
297− },
298− "fast-deep-equal": {
299− "version": "2.0.1",
300− "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
301− "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=",
302− "dev": true
303− },
304− "fast-json-stable-stringify": {
305− "version": "2.0.0",
306− "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
307− "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=",
308− "dev": true
309− },
310− "forever-agent": {
311− "version": "0.6.1",
312− "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
313− "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=",
314 "dev": true
254 "dev": true
315 },
255 },
316− "form-data": {
256+ "fd-slicer": {
317− "version": "2.3.3",
257+ "version": "1.1.0",
318− "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
258+ "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
319− "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
259+ "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=",
320 "dev": true,
260 "dev": true,
321 "requires": {
261 "requires": {
322− "asynckit": "0.4.0",
262+ "pend": "~1.2.0"
323− "combined-stream": "1.0.8",
324− "mime-types": "2.1.24"
325 }
263 }
326 },
264 },
327 "fs.realpath": {
265 "fs.realpath": {
@@ -330,49 +268,18 @@
330 "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
268 "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
331 "dev": true
269 "dev": true
332 },
270 },
333− "getpass": {
334− "version": "0.1.7",
335− "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
336− "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
337− "dev": true,
338− "requires": {
339− "assert-plus": "1.0.0"
340− }
341− },
342 "glob": {
271 "glob": {
343 "version": "7.1.4",
272 "version": "7.1.4",
344 "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz",
273 "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz",
345 "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==",
274 "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==",
346 "dev": true,
275 "dev": true,
347 "requires": {
276 "requires": {
348− "fs.realpath": "1.0.0",
277+ "fs.realpath": "^1.0.0",
349− "inflight": "1.0.6",
278+ "inflight": "^1.0.4",
350− "inherits": "2.0.3",
279+ "inherits": "2",
351− "minimatch": "3.0.4",
280+ "minimatch": "^3.0.4",
352− "once": "1.4.0",
281+ "once": "^1.3.0",
353− "path-is-absolute": "1.0.1"
282+ "path-is-absolute": "^1.0.0"
354− }
355− },
356− "growl": {
357− "version": "1.10.3",
358− "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.3.tgz",
359− "integrity": "sha512-hKlsbA5Vu3xsh1Cg3J7jSmX/WaW6A5oBeqzM88oNbCRQFz+zUaXm6yxS4RVytp1scBoJzSYl4YAEOQIt6O8V1Q==",
360− "dev": true
361− },
362− "har-schema": {
363− "version": "2.0.0",
364− "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
365− "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=",
366− "dev": true
367− },
368− "har-validator": {
369− "version": "5.1.3",
370− "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz",
371− "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==",
372− "dev": true,
373− "requires": {
374− "ajv": "6.10.0",
375− "har-schema": "2.0.0"
376 }
283 }
377 },
284 },
378 "has-flag": {
285 "has-flag": {
@@ -381,41 +288,18 @@
381 "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
288 "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
382 "dev": true
289 "dev": true
383 },
290 },
384− "he": {
291+ "htmlparser2": {
385− "version": "1.1.1",
292+ "version": "3.10.1",
386− "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz",
293+ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz",
387− "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=",
294+ "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==",
388− "dev": true
389− },
390− "http-proxy-agent": {
391− "version": "2.1.0",
392− "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz",
393− "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==",
394 "dev": true,
295 "dev": true,
395 "requires": {
296 "requires": {
396− "agent-base": "4.2.1",
297+ "domelementtype": "^1.3.1",
397− "debug": "3.1.0"
298+ "domhandler": "^2.3.0",
398− }
299+ "domutils": "^1.5.1",
399− },
300+ "entities": "^1.1.1",
400− "http-signature": {
301+ "inherits": "^2.0.1",
401− "version": "1.2.0",
302+ "readable-stream": "^3.1.1"
402− "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
403− "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
404− "dev": true,
405− "requires": {
406− "assert-plus": "1.0.0",
407− "jsprim": "1.4.1",
408− "sshpk": "1.16.1"
409− }
410− },
411− "https-proxy-agent": {
412− "version": "2.2.1",
413− "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz",
414− "integrity": "sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ==",
415− "dev": true,
416− "requires": {
417− "agent-base": "4.2.1",
418− "debug": "3.1.0"
419 }
303 }
420 },
304 },
421 "inflight": {
305 "inflight": {
@@ -424,26 +308,14 @@
424 "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
308 "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
425 "dev": true,
309 "dev": true,
426 "requires": {
310 "requires": {
427− "once": "1.4.0",
311+ "once": "^1.3.0",
428− "wrappy": "1.0.2"
312+ "wrappy": "1"
429 }
313 }
430 },
314 },
431 "inherits": {
315 "inherits": {
432− "version": "2.0.3",
316+ "version": "2.0.4",
433− "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
317+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
434− "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
318+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
435− "dev": true
436− },
437− "is-typedarray": {
438− "version": "1.0.0",
439− "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
440− "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
441− "dev": true
442− },
443− "isstream": {
444− "version": "0.1.2",
445− "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
446− "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=",
447 "dev": true
319 "dev": true
448 },
320 },
449 "js-tokens": {
321 "js-tokens": {
@@ -458,68 +330,57 @@
458 "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
330 "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
459 "dev": true,
331 "dev": true,
460 "requires": {
332 "requires": {
461− "argparse": "1.0.10",
333+ "argparse": "^1.0.7",
462− "esprima": "4.0.1"
334+ "esprima": "^4.0.0"
463 }
335 }
464 },
336 },
465− "jsbn": {
337+ "linkify-it": {
466− "version": "0.1.1",
467− "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
468− "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
469− "dev": true
470− },
471− "json-schema": {
472− "version": "0.2.3",
338+ "version": "2.2.0",
473− "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
339+ "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.2.0.tgz",
474− "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=",
340+ "integrity": "sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw==",
475− "dev": true
476− },
477− "json-schema-traverse": {
478− "version": "0.4.1",
479− "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
480− "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
481− "dev": true
482− },
483− "json-stringify-safe": {
484− "version": "5.0.1",
485− "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
486− "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=",
487− "dev": true
488− },
489− "jsprim": {
490− "version": "1.4.1",
491− "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
492− "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
493 "dev": true,
341 "dev": true,
494 "requires": {
342 "requires": {
495− "assert-plus": "1.0.0",
343+ "uc.micro": "^1.0.1"
496− "extsprintf": "1.3.0",
497− "json-schema": "0.2.3",
498− "verror": "1.10.0"
499 }
344 }
500 },
345 },
501− "mime-db": {
346+ "lodash": {
502− "version": "1.40.0",
347+ "version": "4.17.15",
503− "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz",
348+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
504− "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==",
349+ "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
505 "dev": true
350 "dev": true
506 },
351 },
507− "mime-types": {
352+ "markdown-it": {
508− "version": "2.1.24",
353+ "version": "8.4.2",
509− "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz",
354+ "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-8.4.2.tgz",
510− "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==",
355+ "integrity": "sha512-GcRz3AWTqSUphY3vsUqQSFMbgR38a4Lh3GWlHRh/7MRwz8mcu9n2IO7HOh+bXHrR9kOPDl5RNCaEsrneb+xhHQ==",
511 "dev": true,
356 "dev": true,
512 "requires": {
357 "requires": {
513− "mime-db": "1.40.0"
358+ "argparse": "^1.0.7",
359+ "entities": "~1.1.1",
360+ "linkify-it": "^2.0.0",
361+ "mdurl": "^1.0.1",
362+ "uc.micro": "^1.0.5"
514 }
363 }
515 },
364 },
365+ "mdurl": {
366+ "version": "1.0.1",
367+ "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz",
368+ "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=",
369+ "dev": true
370+ },
371+ "mime": {
372+ "version": "1.6.0",
373+ "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
374+ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
375+ "dev": true
376+ },
516 "minimatch": {
377 "minimatch": {
517 "version": "3.0.4",
378 "version": "3.0.4",
518 "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
379 "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
519 "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
380 "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
520 "dev": true,
381 "dev": true,
521 "requires": {
382 "requires": {
522− "brace-expansion": "1.1.11"
383+ "brace-expansion": "^1.1.7"
523 }
384 }
524 },
385 },
525 "minimist": {
386 "minimist": {
@@ -537,86 +398,82 @@
537 "minimist": "0.0.8"
398 "minimist": "0.0.8"
538 }
399 }
539 },
400 },
540− "mocha": {
401+ "mute-stream": {
541− "version": "4.1.0",
402+ "version": "0.0.8",
403+ "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
404+ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==",
405+ "dev": true
406+ },
407+ "nth-check": {
408+ "version": "1.0.2",
542− "resolved": "https://registry.npmjs.org/mocha/-/mocha-4.1.0.tgz",
409+ "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz",
543− "integrity": "sha512-0RVnjg1HJsXY2YFDoTNzcc1NKhYuXKRrBAG2gDygmJJA136Cs2QlRliZG1mA0ap7cuaT30mw16luAeln+4RiNA==",
410+ "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==",
544 "dev": true,
411 "dev": true,
545 "requires": {
412 "requires": {
546− "browser-stdout": "1.3.0",
413+ "boolbase": "~1.0.0"
547− "commander": "2.11.0",
548− "debug": "3.1.0",
549− "diff": "3.3.1",
550− "escape-string-regexp": "1.0.5",
551− "glob": "7.1.2",
552− "growl": "1.10.3",
553− "he": "1.1.1",
554− "mkdirp": "0.5.1",
555− "supports-color": "4.4.0"
556− },
557− "dependencies": {
558− "commander": {
559− "version": "2.11.0",
560− "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz",
561− "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==",
562− "dev": true
563− },
564− "diff": {
565− "version": "3.3.1",
566− "resolved": "https://registry.npmjs.org/diff/-/diff-3.3.1.tgz",
567− "integrity": "sha512-MKPHZDMB0o6yHyDryUOScqZibp914ksXwAMYMTHj6KO8UeKsRYNJD3oNCKjTqZon+V488P7N/HzXF8t7ZR95ww==",
568− "dev": true
569− },
570− "glob": {
571− "version": "7.1.2",
572− "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
573− "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
574− "dev": true,
575− "requires": {
576− "fs.realpath": "1.0.0",
577− "inflight": "1.0.6",
578− "inherits": "2.0.3",
579− "minimatch": "3.0.4",
580− "once": "1.4.0",
581− "path-is-absolute": "1.0.1"
582− }
583− },
584− "has-flag": {
585− "version": "2.0.0",
586− "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz",
587− "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=",
588− "dev": true
589− },
590− "supports-color": {
591− "version": "4.4.0",
592− "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz",
593− "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==",
594− "dev": true,
595− "requires": {
596− "has-flag": "2.0.0"
597− }
598− }
599 }
414 }
600 },
415 },
601− "ms": {
602− "version": "2.0.0",
603− "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
604− "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
605− "dev": true
606− },
607− "oauth-sign": {
608− "version": "0.9.0",
609− "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
610− "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==",
611− "dev": true
612− },
613 "once": {
416 "once": {
614 "version": "1.4.0",
417 "version": "1.4.0",
615 "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
418 "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
616 "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
419 "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
617 "dev": true,
420 "dev": true,
618 "requires": {
421 "requires": {
619− "wrappy": "1.0.2"
422+ "wrappy": "1"
423+ }
424+ },
425+ "os": {
426+ "version": "0.1.1",
427+ "resolved": "https://registry.npmjs.org/os/-/os-0.1.1.tgz",
428+ "integrity": "sha1-IIhF6J4ZOtTZcUdLk5R3NqVtE/M=",
429+ "dev": true
430+ },
431+ "os-homedir": {
432+ "version": "1.0.2",
433+ "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
434+ "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
435+ "dev": true
436+ },
437+ "os-tmpdir": {
438+ "version": "1.0.2",
439+ "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
440+ "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
441+ "dev": true
442+ },
443+ "osenv": {
444+ "version": "0.1.5",
445+ "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz",
446+ "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
447+ "dev": true,
448+ "requires": {
449+ "os-homedir": "^1.0.0",
450+ "os-tmpdir": "^1.0.0"
451+ }
452+ },
453+ "parse-semver": {
454+ "version": "1.1.1",
455+ "resolved": "https://registry.npmjs.org/parse-semver/-/parse-semver-1.1.1.tgz",
456+ "integrity": "sha1-mkr9bfBj3Egm+T+6SpnPIj9mbLg=",
457+ "dev": true,
458+ "requires": {
459+ "semver": "^5.1.0"
460+ },
461+ "dependencies": {
462+ "semver": {
463+ "version": "5.7.1",
464+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
465+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
466+ "dev": true
467+ }
468+ }
469+ },
470+ "parse5": {
471+ "version": "3.0.3",
472+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-3.0.3.tgz",
473+ "integrity": "sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA==",
474+ "dev": true,
475+ "requires": {
476+ "@types/node": "*"
620 }
477 }
621 },
478 },
622 "path-is-absolute": {
479 "path-is-absolute": {
@@ -631,112 +488,51 @@
631 "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
488 "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
632 "dev": true
489 "dev": true
633 },
490 },
634− "performance-now": {
491+ "pend": {
635− "version": "2.1.0",
492+ "version": "1.2.0",
636− "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
493+ "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
637− "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=",
494+ "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=",
638− "dev": true
639− },
640− "psl": {
641− "version": "1.1.32",
642− "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.32.tgz",
643− "integrity": "sha512-MHACAkHpihU/REGGPLj4sEfc/XKW2bheigvHO1dUqjaKigMp1C8+WLQYRGgeKFMsw5PMfegZcaN8IDXK/cD0+g==",
644− "dev": true
645− },
646− "punycode": {
647− "version": "2.1.1",
648− "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
649− "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
650− "dev": true
651− },
652− "qs": {
653− "version": "6.5.2",
654− "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
655− "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==",
656− "dev": true
657− },
658− "querystringify": {
659− "version": "2.1.1",
660− "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.1.tgz",
661− "integrity": "sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==",
662 "dev": true
495 "dev": true
663 },
496 },
664− "request": {
497+ "read": {
665− "version": "2.88.0",
498+ "version": "1.0.7",
666− "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz",
499+ "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz",
667− "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==",
500+ "integrity": "sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ=",
668 "dev": true,
501 "dev": true,
669 "requires": {
502 "requires": {
670− "aws-sign2": "0.7.0",
671− "aws4": "1.8.0",
672− "caseless": "0.12.0",
673− "combined-stream": "1.0.8",
503+ "mute-stream": "~0.0.4"
674− "extend": "3.0.2",
675− "forever-agent": "0.6.1",
676− "form-data": "2.3.3",
677− "har-validator": "5.1.3",
678− "http-signature": "1.2.0",
679− "is-typedarray": "1.0.0",
680− "isstream": "0.1.2",
681− "json-stringify-safe": "5.0.1",
682− "mime-types": "2.1.24",
683− "oauth-sign": "0.9.0",
684− "performance-now": "2.1.0",
685− "qs": "6.5.2",
686− "safe-buffer": "5.1.2",
687− "tough-cookie": "2.4.3",
688− "tunnel-agent": "0.6.0",
689− "uuid": "3.3.2"
690 }
504 }
691 },
505 },
692− "requires-port": {
506+ "readable-stream": {
693− "version": "1.0.0",
507+ "version": "3.4.0",
694− "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
508+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz",
695− "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=",
509+ "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==",
696− "dev": true
510+ "dev": true,
511+ "requires": {
512+ "inherits": "^2.0.3",
513+ "string_decoder": "^1.1.1",
514+ "util-deprecate": "^1.0.1"
515+ }
697 },
516 },
698 "resolve": {
517 "resolve": {
699− "version": "1.11.0",
518+ "version": "1.12.0",
700− "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.0.tgz",
519+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz",
701− "integrity": "sha512-WL2pBDjqT6pGUNSUzMw00o4T7If+z4H2x3Gz893WoUQ5KW8Vr9txp00ykiP16VBaZF5+j/OcXJHZ9+PCvdiDKw==",
520+ "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==",
702 "dev": true,
521 "dev": true,
703 "requires": {
522 "requires": {
704− "path-parse": "1.0.6"
523+ "path-parse": "^1.0.6"
705 }
524 }
706 },
525 },
707 "safe-buffer": {
526 "safe-buffer": {
708− "version": "5.1.2",
527+ "version": "5.2.0",
709− "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
528+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz",
710− "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
529+ "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==",
711− "dev": true
712− },
713− "safer-buffer": {
714− "version": "2.1.2",
715− "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
716− "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
717 "dev": true
530 "dev": true
718 },
531 },
719 "semver": {
532 "semver": {
720− "version": "5.7.0",
533+ "version": "6.3.0",
721− "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz",
534+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
722− "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==",
535+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
723− "dev": true
724− },
725− "source-map": {
726− "version": "0.6.1",
727− "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
728− "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
729− "dev": true
730− },
731− "source-map-support": {
732− "version": "0.5.12",
733− "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.12.tgz",
734− "integrity": "sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ==",
735− "dev": true,
736− "requires": {
737− "buffer-from": "1.1.1",
738− "source-map": "0.6.1"
739− }
740 },
536 },
741 "sprintf-js": {
537 "sprintf-js": {
742 "version": "1.0.3",
538 "version": "1.0.3",
@@ -744,21 +540,13 @@
744 "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
540 "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
745 "dev": true
541 "dev": true
746 },
542 },
747− "sshpk": {
543+ "string_decoder": {
748− "version": "1.16.1",
544+ "version": "1.3.0",
749− "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz",
545+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
750− "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==",
546+ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
751 "dev": true,
547 "dev": true,
752 "requires": {
548 "requires": {
753− "asn1": "0.2.4",
754− "assert-plus": "1.0.0",
755− "bcrypt-pbkdf": "1.0.2",
756− "dashdash": "1.14.1",
757− "ecc-jsbn": "0.1.2",
758− "getpass": "0.1.7",
759− "jsbn": "0.1.1",
760− "safer-buffer": "2.1.2",
549+ "safe-buffer": "~5.2.0"
761− "tweetnacl": "0.14.5"
762 }
550 }
763 },
551 },
764 "supports-color": {
552 "supports-color": {
@@ -767,25 +555,16 @@
767 "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
555 "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
768 "dev": true,
556 "dev": true,
769 "requires": {
557 "requires": {
770− "has-flag": "3.0.0"
558+ "has-flag": "^3.0.0"
771 }
559 }
772 },
560 },
773− "tough-cookie": {
561+ "tmp": {
774− "version": "2.4.3",
562+ "version": "0.0.29",
775− "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz",
563+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.29.tgz",
776− "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==",
564+ "integrity": "sha1-8lEl/w3Z2jzLDC3Tce4SiLuRKMA=",
777 "dev": true,
565 "dev": true,
778 "requires": {
566 "requires": {
779− "psl": "1.1.32",
567+ "os-tmpdir": "~1.0.1"
780− "punycode": "1.4.1"
781− },
782− "dependencies": {
783− "punycode": {
784− "version": "1.4.1",
785− "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
786− "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
787− "dev": true
788− }
789 }
568 }
790 },
569 },
791 "tslib": {
570 "tslib": {
@@ -795,24 +574,32 @@
795 "dev": true
574 "dev": true
796 },
575 },
797 "tslint": {
576 "tslint": {
798− "version": "5.17.0",
577+ "version": "5.20.0",
799− "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.17.0.tgz",
578+ "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.20.0.tgz",
800− "integrity": "sha512-pflx87WfVoYepTet3xLfDOLDm9Jqi61UXIKePOuca0qoAZyrGWonDG9VTbji58Fy+8gciUn8Bt7y69+KEVjc/w==",
579+ "integrity": "sha512-2vqIvkMHbnx8acMogAERQ/IuINOq6DFqgF8/VDvhEkBqQh/x6SP0Y+OHnKth9/ZcHQSroOZwUQSN18v8KKF0/g==",
801− "dev": true,
580+ "dev": true,
802− "requires": {
581+ "requires": {
803− "@babel/code-frame": "7.0.0",
582+ "@babel/code-frame": "^7.0.0",
804− "builtin-modules": "1.1.1",
583+ "builtin-modules": "^1.1.1",
805− "chalk": "2.4.2",
584+ "chalk": "^2.3.0",
806− "commander": "2.20.0",
585+ "commander": "^2.12.1",
807− "diff": "3.5.0",
586+ "diff": "^4.0.1",
808− "glob": "7.1.4",
587+ "glob": "^7.1.1",
809− "js-yaml": "3.13.1",
588+ "js-yaml": "^3.13.1",
810− "minimatch": "3.0.4",
589+ "minimatch": "^3.0.4",
811− "mkdirp": "0.5.1",
590+ "mkdirp": "^0.5.1",
812− "resolve": "1.11.0",
591+ "resolve": "^1.3.2",
813− "semver": "5.7.0",
592+ "semver": "^5.3.0",
814− "tslib": "1.10.0",
593+ "tslib": "^1.8.0",
815− "tsutils": "2.29.0"
594+ "tsutils": "^2.29.0"
595+ },
596+ "dependencies": {
597+ "semver": {
598+ "version": "5.7.1",
599+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
600+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
601+ "dev": true
602+ }
816 }
603 }
817 },
604 },
818 "tsutils": {
605 "tsutils": {
@@ -821,123 +608,143 @@
821 "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==",
608 "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==",
822 "dev": true,
609 "dev": true,
823 "requires": {
610 "requires": {
824− "tslib": "1.10.0"
611+ "tslib": "^1.8.1"
825 }
612 }
826 },
613 },
827− "tunnel-agent": {
614+ "tunnel": {
828− "version": "0.6.0",
615+ "version": "0.0.4",
829− "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
616+ "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.4.tgz",
830− "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
617+ "integrity": "sha1-LTeFoVjBdMmhbcLARuxfxfF0IhM=",
618+ "dev": true
619+ },
620+ "typed-rest-client": {
621+ "version": "1.2.0",
622+ "resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.2.0.tgz",
623+ "integrity": "sha512-FrUshzZ1yxH8YwGR29PWWnfksLEILbWJydU7zfIRkyH7kAEzB62uMAl2WY6EyolWpLpVHeJGgQm45/MaruaHpw==",
831 "dev": true,
624 "dev": true,
832 "requires": {
625 "requires": {
833− "safe-buffer": "5.1.2"
626+ "tunnel": "0.0.4",
627+ "underscore": "1.8.3"
834 }
628 }
835 },
629 },
836− "tweetnacl": {
837− "version": "0.14.5",
838− "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
839− "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
840− "dev": true
841− },
842 "typescript": {
630 "typescript": {
843− "version": "3.4.5",
631+ "version": "3.6.3",
844− "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.4.5.tgz",
632+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.3.tgz",
845− "integrity": "sha512-YycBxUb49UUhdNMU5aJ7z5Ej2XGmaIBL0x34vZ82fn3hGvD+bgrMrVDpatgz2f7YxUMJxMkbWxJZeAvDxVe7Vw==",
633+ "integrity": "sha512-N7bceJL1CtRQ2RiG0AQME13ksR7DiuQh/QehubYcghzv20tnh+MQnQIuJddTmsbqYj+dztchykemz0zFzlvdQw==",
846 "dev": true
634 "dev": true
847 },
635 },
848− "uri-js": {
636+ "uc.micro": {
849− "version": "4.2.2",
850− "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
851− "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
852− "dev": true,
853− "requires": {
854− "punycode": "2.1.1"
855− }
856− },
857− "url-parse": {
858− "version": "1.4.7",
637+ "version": "1.0.6",
859− "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz",
638+ "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",
860− "integrity": "sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==",
639+ "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==",
861− "dev": true,
640+ "dev": true
862− "requires": {
863− "querystringify": "2.1.1",
864− "requires-port": "1.0.0"
865− }
866 },
641 },
867− "uuid": {
642+ "underscore": {
868− "version": "3.3.2",
643+ "version": "1.8.3",
869− "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
644+ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz",
870− "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==",
645+ "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=",
871 "dev": true
646 "dev": true
872 },
647 },
873− "verror": {
648+ "url-join": {
874− "version": "1.10.0",
649+ "version": "1.1.0",
875− "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
650+ "resolved": "https://registry.npmjs.org/url-join/-/url-join-1.1.0.tgz",
876− "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
651+ "integrity": "sha1-dBxsL0WWxIMNZxhGCSDQySIC3Hg=",
877− "dev": true,
652+ "dev": true
878− "requires": {
879− "assert-plus": "1.0.0",
880− "core-util-is": "1.0.2",
881− "extsprintf": "1.3.0"
882− }
883 },
653 },
884− "vscode": {
654+ "util-deprecate": {
885− "version": "1.1.34",
655+ "version": "1.0.2",
656+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
657+ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
658+ "dev": true
659+ },
660+ "vsce": {
661+ "version": "1.68.0",
886− "resolved": "https://registry.npmjs.org/vscode/-/vscode-1.1.34.tgz",
662+ "resolved": "https://registry.npmjs.org/vsce/-/vsce-1.68.0.tgz",
887− "integrity": "sha512-GuT3tCT2N5Qp26VG4C+iGmWMgg/MuqtY5G5TSOT3U/X6pgjM9LFulJEeqpyf6gdzpI4VyU3ZN/lWPo54UFPuQg==",
663+ "integrity": "sha512-yFbRYu4x4GbdQzZdEQQeRJBxgPdummgcUOFHUtnclW8XQl3MTuKgXL3TtI09gb5oq7jE6kdyvBmpBcmDGsmhcQ==",
888− "dev": true,
664+ "dev": true,
889− "requires": {
665+ "requires": {
666+ "azure-devops-node-api": "^7.2.0",
667+ "chalk": "^2.4.2",
668+ "cheerio": "^1.0.0-rc.1",
669+ "commander": "^2.8.1",
670+ "denodeify": "^1.2.1",
671+ "didyoumean": "^1.2.1",
890− "glob": "7.1.4",
672+ "glob": "^7.0.6",
891− "mocha": "4.1.0",
673+ "lodash": "^4.17.10",
892− "request": "2.88.0",
674+ "markdown-it": "^8.3.1",
675+ "mime": "^1.3.4",
676+ "minimatch": "^3.0.3",
677+ "osenv": "^0.1.3",
678+ "parse-semver": "^1.1.1",
679+ "read": "^1.0.7",
893− "semver": "5.7.0",
680+ "semver": "^5.1.0",
894− "source-map-support": "0.5.12",
681+ "tmp": "0.0.29",
895− "url-parse": "1.4.7",
682+ "typed-rest-client": "1.2.0",
896− "vscode-test": "0.4.1"
683+ "url-join": "^1.1.0",
684+ "yauzl": "^2.3.1",
685+ "yazl": "^2.2.2"
686+ },
687+ "dependencies": {
688+ "semver": {
689+ "version": "5.7.1",
690+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
691+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
692+ "dev": true
693+ }
897 }
694 }
898 },
695 },
899 "vscode-jsonrpc": {
696 "vscode-jsonrpc": {
900− "version": "4.0.0",
697+ "version": "5.0.0-next.2",
901− "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-4.0.0.tgz",
698+ "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-5.0.0-next.2.tgz",
902− "integrity": "sha512-perEnXQdQOJMTDFNv+UF3h1Y0z4iSiaN9jIlb0OqIYgosPCZGYh/MCUlkFtV2668PL69lRDO32hmvL2yiidUYg=="
699+ "integrity": "sha512-Q3/jabZUNviCG9hhF6hHWjhrABevPF9mv0aiE2j8BYCAP2k+aHTpjMyk+04MzaAqWYwXdQuZkLSbcYCCqbzJLg=="
903 },
700 },
904 "vscode-languageclient": {
701 "vscode-languageclient": {
905− "version": "4.4.2",
702+ "version": "6.0.0-next.1",
906− "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-4.4.2.tgz",
703+ "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-6.0.0-next.1.tgz",
907− "integrity": "sha512-9TUzsg1UM6n1UEyPlWbDf7tK1wJAK7UGFRmGDN8sz4KmbbDiVRh6YicaB/5oRSVTpuV47PdJpYlOl3SJ0RiK1Q==",
704+ "integrity": "sha512-eJ9VjLFNINArgRzLbQ11YlWry7dM93GEODkQBXTRfrSypksiO9qSGr4SHhWgxxP26p4FRSpzc/17+N+Egnnchg==",
908 "requires": {
705 "requires": {
706+ "semver": "^6.3.0",
909− "vscode-languageserver-protocol": "3.14.1"
707+ "vscode-languageserver-protocol": "^3.15.0-next.9"
910 }
708 }
911 },
709 },
912 "vscode-languageserver-protocol": {
710 "vscode-languageserver-protocol": {
913− "version": "3.14.1",
711+ "version": "3.15.0-next.9",
914− "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.14.1.tgz",
712+ "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.0-next.9.tgz",
915− "integrity": "sha512-IL66BLb2g20uIKog5Y2dQ0IiigW0XKrvmWiOvc0yXw80z3tMEzEnHjaGAb3ENuU7MnQqgnYJ1Cl2l9RvNgDi4g==",
713+ "integrity": "sha512-b9PAxouMmtsLEe8ZjbIMPb7wRWPhckGfgjwZLmp/dWnaAuRPYtY3lGO0/rNbLc3jKIqCVlnEyYVFKalzDAzj0g==",
916 "requires": {
714 "requires": {
917− "vscode-jsonrpc": "4.0.0",
715+ "vscode-jsonrpc": "^5.0.0-next.2",
918− "vscode-languageserver-types": "3.14.0"
716+ "vscode-languageserver-types": "^3.15.0-next.5"
919 }
717 }
920 },
718 },
921 "vscode-languageserver-types": {
719 "vscode-languageserver-types": {
922− "version": "3.14.0",
720+ "version": "3.15.0-next.5",
923− "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.14.0.tgz",
721+ "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.15.0-next.5.tgz",
924− "integrity": "sha512-lTmS6AlAlMHOvPQemVwo3CezxBp0sNB95KNPkqp3Nxd5VFEnuG1ByM0zlRWos0zjO3ZWtkvhal0COgiV1xIA4A=="
722+ "integrity": "sha512-7hrELhTeWieUgex3+6692KjCkcmO/+V/bFItM5MHGcBotzwmjEuXjapLLYTYhIspuJ1ibRSik5MhX5YwLpsPiw=="
925− },
926− "vscode-test": {
927− "version": "0.4.1",
928− "resolved": "https://registry.npmjs.org/vscode-test/-/vscode-test-0.4.1.tgz",
929− "integrity": "sha512-uIi/07uG/gmCbD9Y9bFpNzmk4el82xiclijEdL426A3jOFfvwdqgfmtuWYfxEGo0w6JY9EqVDTGQCXwuInXVTQ==",
930− "dev": true,
931− "requires": {
932− "http-proxy-agent": "2.1.0",
933− "https-proxy-agent": "2.2.1"
934− }
935 },
723 },
936 "wrappy": {
724 "wrappy": {
937 "version": "1.0.2",
725 "version": "1.0.2",
938 "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
726 "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
939 "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
727 "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
940 "dev": true
728 "dev": true
729+ },
730+ "yauzl": {
731+ "version": "2.10.0",
732+ "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
733+ "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=",
734+ "dev": true,
735+ "requires": {
736+ "buffer-crc32": "~0.2.3",
737+ "fd-slicer": "~1.1.0"
738+ }
739+ },
740+ "yazl": {
741+ "version": "2.5.1",
742+ "resolved": "https://registry.npmjs.org/yazl/-/yazl-2.5.1.tgz",
743+ "integrity": "sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw==",
744+ "dev": true,
745+ "requires": {
746+ "buffer-crc32": "~0.2.3"
747+ }
941 }
748 }
942 }
749 }
943}
750}
extension/package.json+31−24
@@ -1,13 +1,14 @@
1{
1{
2− "name": "hie-core",
2+ "name": "ghcide",
3− "displayName": "hie-core",
3+ "displayName": "ghcide",
4− "publisher": "digitalasset",
4+ "publisher": "DigitalAssetHoldingsLLC",
5 "repository": {
5 "repository": {
6− "type" : "git",
6+ "type": "git",
7− "url" : "https://github.com/digitalasset/daml.git"
7+ "url": "https://github.com/digital-asset/ghcide.git"
8 },
8 },
9 "description": "A simple extension to test out haskell ide core",
9 "description": "A simple extension to test out haskell ide core",
10− "version": "0.0.1",
10+ "version": "0.0.2",
11+ "license": "Apache-2.0",
11 "engines": {
12 "engines": {
12 "vscode": "^1.35.0"
13 "vscode": "^1.35.0"
13 },
14 },
@@ -15,24 +16,29 @@
15 "Other"
16 "Other"
16 ],
17 ],
17 "activationEvents": [
18 "activationEvents": [
18− "onLanguage:haskell"
19+ "onLanguage:haskell"
19 ],
20 ],
20 "main": "./out/extension.js",
21 "main": "./out/extension.js",
21 "contributes": {
22 "contributes": {
22− "languages": [{
23+ "languages": [
24+ {
23− "id": "haskell",
25+ "id": "haskell",
24− "extensions": [
26+ "extensions": [
27+ "hs",
25− "hs"
28+ "hs-boot",
29+ "lhs-boot",
30+ "lhs"
26− ]
31+ ]
32+ }
27− }],
33+ ],
28 "configuration": {
34 "configuration": {
29 "type": "object",
35 "type": "object",
30 "title": "Haskell IDE Core Configuration",
36 "title": "Haskell IDE Core Configuration",
31 "properties": {
37 "properties": {
32 "hic.executablePath": {
38 "hic.executablePath": {
33 "type": "string",
39 "type": "string",
34− "default": "hie-core",
40+ "default": "ghcide",
35− "description": "The location of your hie-core executable"
41+ "description": "The location of your ghcide executable"
36 },
42 },
37 "hic.arguments": {
43 "hic.arguments": {
38 "type": "string",
44 "type": "string",
@@ -46,20 +52,21 @@
46 "vscode:prepublish": "npm run compile",
52 "vscode:prepublish": "npm run compile",
47 "compile": "tsc -p ./",
53 "compile": "tsc -p ./",
48 "watch": "tsc -watch -p ./",
54 "watch": "tsc -watch -p ./",
49− "postinstall": "node ./node_modules/vscode/bin/install",
50− "test": "npm run compile && node ./node_modules/vscode/bin/test"
55+ "test": "npm run compile && node ./node_modules/vscode/bin/test",
56+ "vscepackage": "vsce package"
51 },
57 },
52− "extensionDependencies": [
58+ "extensionDependencies": [
53− "justusadam.language-haskell"
59+ "justusadam.language-haskell"
54 ],
60 ],
55 "dependencies": {
61 "dependencies": {
56− "vscode-languageclient": "^4.1.4"
62+ "vscode-languageclient": "^6.0.0-next.1"
57 },
63 },
58 "devDependencies": {
64 "devDependencies": {
59− "typescript": "^3.3.1",
65+ "@types/mocha": "^5.2.7",
66+ "@types/node": "^12.7.11",
60− "vscode": "^1.1.28",
67+ "@types/vscode": "1.35.0",
61 "tslint": "^5.12.1",
68 "tslint": "^5.12.1",
62− "@types/node": "^10.12.21",
69+ "typescript": "^3.6.3",
63− "@types/mocha": "^2.2.42"
70+ "vsce": "^1.68.0"
64 }
71 }
65}
72}
extension/src/extension.ts+5−4
@@ -1,4 +1,4 @@
1−// Copyright (c) 2019 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
1+// Copyright (c) 2019 The DAML Authors. All rights reserved.
2// SPDX-License-Identifier: Apache-2.0
2// SPDX-License-Identifier: Apache-2.0
3
3
4import * as path from 'path';
4import * as path from 'path';
@@ -28,12 +28,13 @@ export function activate(context: ExtensionContext) {
28 // Register the server for plain text documents
28 // Register the server for plain text documents
29 documentSelector: ["haskell"]
29 documentSelector: ["haskell"]
30 };
30 };
31− let client2 = new LanguageClient(
31+ client = new LanguageClient(
32 'haskell',
32 'haskell',
33− 'Haskell IDE Core',
33+ 'ghcide',
34 { args: args, command: cPath, options: {cwd: workspace.rootPath }}, clientOptions, true);
34 { args: args, command: cPath, options: {cwd: workspace.rootPath }}, clientOptions, true);
35+ client.registerProposedFeatures();
35
36
36− client2.start();
37+ client.start();
37}
38}
38
39
39export function deactivate(): Thenable<void> | undefined {
40export function deactivate(): Thenable<void> | undefined {
fmt.shnew file+3
@@ -0,0 +1,3 @@
1+#!/usr/bin/env bash
2+set -eou pipefail
3+curl -sSL https://raw.github.com/ndmitchell/hlint/master/misc/run.sh | sh -s . --with-group=extra
ghcide.cabalnew file+344
@@ -0,0 +1,344 @@
1+cabal-version: 1.20
2+build-type: Simple
3+category: Development
4+name: ghcide
5+version: 0.2.0
6+license: Apache-2.0
7+license-file: LICENSE
8+author: Digital Asset
9+maintainer: Digital Asset
10+copyright: Digital Asset 2018-2020
11+synopsis: The core of an IDE
12+description:
13+ A library for building Haskell IDE's on top of the GHC API.
14+homepage: https://github.com/digital-asset/ghcide#readme
15+bug-reports: https://github.com/digital-asset/ghcide/issues
16+tested-with: GHC==8.6.5
17+extra-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
25+
26+source-repository head
27+ type: git
28+ location: https://github.com/digital-asset/ghcide.git
29+
30+flag ghc-lib
31+ description: build against ghc-lib instead of the ghc package
32+ default: False
33+ manual: True
34+
35+library
36+ default-language: Haskell2010
37+ build-depends:
38+ aeson,
39+ array,
40+ async,
41+ base == 4.*,
42+ binary,
43+ bytestring,
44+ containers,
45+ data-default,
46+ deepseq,
47+ directory,
48+ extra,
49+ fuzzy,
50+ filepath,
51+ haddock-library >= 1.8,
52+ hashable,
53+ haskell-lsp-types == 0.22.*,
54+ haskell-lsp == 0.22.*,
55+ mtl,
56+ network-uri,
57+ prettyprinter-ansi-terminal,
58+ prettyprinter-ansi-terminal,
59+ prettyprinter,
60+ regex-tdfa >= 1.3.1.0,
61+ rope-utf16-splay,
62+ safe-exceptions,
63+ shake >= 0.18.4,
64+ sorted-list,
65+ stm,
66+ syb,
67+ text,
68+ time,
69+ transformers,
70+ unordered-containers >= 0.2.10.0,
71+ utf8-string,
72+ hslogger
73+ if flag(ghc-lib)
74+ build-depends:
75+ ghc-lib >= 8.8,
76+ ghc-lib-parser >= 8.8
77+ cpp-options: -DGHC_LIB
78+ else
79+ build-depends:
80+ ghc-boot-th,
81+ ghc-boot,
82+ ghc >= 8.4
83+ if os(windows)
84+ build-depends:
85+ Win32
86+ else
87+ build-depends:
88+ unix
89+ c-sources:
90+ cbits/getmodtime.c
91+
92+ default-extensions:
93+ BangPatterns
94+ DeriveFunctor
95+ DeriveGeneric
96+ GeneralizedNewtypeDeriving
97+ LambdaCase
98+ NamedFieldPuns
99+ OverloadedStrings
100+ RecordWildCards
101+ ScopedTypeVariables
102+ StandaloneDeriving
103+ TupleSections
104+ TypeApplications
105+ ViewPatterns
106+
107+ hs-source-dirs:
108+ src
109+ include-dirs:
110+ include
111+ exposed-modules:
112+ Development.IDE.Compat
113+ Development.IDE.Core.Debouncer
114+ Development.IDE.Core.FileStore
115+ Development.IDE.Core.IdeConfiguration
116+ Development.IDE.Core.OfInterest
117+ Development.IDE.Core.PositionMapping
118+ Development.IDE.Core.Rules
119+ Development.IDE.Core.RuleTypes
120+ Development.IDE.Core.Service
121+ Development.IDE.Core.Shake
122+ Development.IDE.GHC.Error
123+ Development.IDE.GHC.Util
124+ Development.IDE.Import.DependencyInformation
125+ Development.IDE.LSP.HoverDefinition
126+ Development.IDE.LSP.LanguageServer
127+ Development.IDE.LSP.Outline
128+ Development.IDE.LSP.Protocol
129+ Development.IDE.LSP.Server
130+ Development.IDE.Spans.Common
131+ Development.IDE.Types.Diagnostics
132+ Development.IDE.Types.Location
133+ Development.IDE.Types.Logger
134+ Development.IDE.Types.Options
135+ Development.IDE.Plugin
136+ Development.IDE.Plugin.Completions
137+ Development.IDE.Plugin.CodeAction
138+ other-modules:
139+ Development.IDE.Core.Compile
140+ Development.IDE.Core.Preprocessor
141+ Development.IDE.Core.FileExists
142+ Development.IDE.GHC.Compat
143+ Development.IDE.GHC.CPP
144+ Development.IDE.GHC.Orphans
145+ Development.IDE.GHC.Warnings
146+ Development.IDE.GHC.WithDynFlags
147+ Development.IDE.Import.FindImports
148+ Development.IDE.LSP.Notifications
149+ Development.IDE.Spans.AtPoint
150+ Development.IDE.Spans.Calculate
151+ Development.IDE.Spans.Documentation
152+ 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
166+ ghc-options: -Wall -Wno-name-shadowing
167+
168+executable ghcide-test-preprocessor
169+ default-language: Haskell2010
170+ hs-source-dirs: test/preprocessor
171+ ghc-options: -Wall -Wno-name-shadowing
172+ main-is: Main.hs
173+ build-depends:
174+ base == 4.*
175+
176+executable ghcide
177+ if flag(ghc-lib)
178+ buildable: False
179+ default-language: Haskell2010
180+ hs-source-dirs: exe
181+ ghc-options:
182+ -threaded
183+ -Wall
184+ -Wno-name-shadowing
185+ -- allow user RTS overrides
186+ -rtsopts
187+ -- disable idle GC
188+ -- disable parallel GC
189+ -- increase nursery size
190+ "-with-rtsopts=-I0 -qg -A128M"
191+ main-is: Main.hs
192+ build-depends:
193+ time,
194+ async,
195+ hslogger,
196+ aeson,
197+ base == 4.*,
198+ binary,
199+ base16-bytestring >=0.1.1 && <0.2,
200+ bytestring,
201+ containers,
202+ cryptohash-sha1 >=0.11.100 && <0.12,
203+ data-default,
204+ deepseq,
205+ directory,
206+ extra,
207+ filepath,
208+ ghc-check >= 0.3.0.1 && < 0.4,
209+ ghc-paths,
210+ ghc,
211+ gitrev,
212+ hashable,
213+ haskell-lsp,
214+ haskell-lsp-types,
215+ hie-bios >= 0.5.0 && < 0.6,
216+ ghcide,
217+ optparse-applicative,
218+ shake,
219+ text,
220+ unordered-containers
221+ other-modules:
222+ Utils
223+ Arguments
224+ Paths_ghcide
225+
226+ default-extensions:
227+ BangPatterns
228+ DeriveFunctor
229+ DeriveGeneric
230+ GeneralizedNewtypeDeriving
231+ LambdaCase
232+ NamedFieldPuns
233+ OverloadedStrings
234+ RecordWildCards
235+ ScopedTypeVariables
236+ StandaloneDeriving
237+ TupleSections
238+ TypeApplications
239+ ViewPatterns
240+
241+test-suite ghcide-tests
242+ if flag(ghc-lib)
243+ buildable: False
244+ type: exitcode-stdio-1.0
245+ default-language: Haskell2010
246+ build-tool-depends:
247+ ghcide:ghcide,
248+ ghcide:ghcide-test-preprocessor
249+ build-depends:
250+ aeson,
251+ base,
252+ bytestring,
253+ containers,
254+ directory,
255+ extra,
256+ filepath,
257+ --------------------------------------------------------------
258+ -- The MIN_GHC_API_VERSION macro relies on MIN_VERSION pragmas
259+ -- which require depending on ghc. So the tests need to depend
260+ -- on ghc if they need to use MIN_GHC_API_VERSION. Maybe a
261+ -- better solution can be found, but this is a quick solution
262+ -- which works for now.
263+ ghc,
264+ --------------------------------------------------------------
265+ ghcide,
266+ ghc-typelits-knownnat,
267+ haddock-library,
268+ haskell-lsp,
269+ haskell-lsp-types,
270+ network-uri,
271+ lens,
272+ lsp-test >= 0.11.0.1 && < 0.12,
273+ parser-combinators,
274+ QuickCheck,
275+ quickcheck-instances,
276+ rope-utf16-splay,
277+ shake,
278+ tasty,
279+ tasty-expected-failure,
280+ tasty-hunit,
281+ tasty-quickcheck,
282+ tasty-rerun,
283+ text
284+ hs-source-dirs: test/cabal test/exe test/src
285+ include-dirs: include
286+ ghc-options: -threaded -Wall -Wno-name-shadowing
287+ main-is: Main.hs
288+ other-modules:
289+ Development.IDE.Test
290+ Development.IDE.Test.Runfiles
291+ default-extensions:
292+ BangPatterns
293+ DeriveFunctor
294+ DeriveGeneric
295+ GeneralizedNewtypeDeriving
296+ LambdaCase
297+ NamedFieldPuns
298+ OverloadedStrings
299+ RecordWildCards
300+ ScopedTypeVariables
301+ StandaloneDeriving
302+ TupleSections
303+ TypeApplications
304+ 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-core.cabaldeleted−129
@@ -1,129 +0,0 @@
1−cabal-version: >= 1.18
2−build-type: Simple
3−name: hie-core
4−version: 0
5−license: BSD3
6−x-license: BSD3 OR Apache2
7−author: Digital Asset
8−maintainer: Digital Asset
9−copyright: Digital Asset 2018-2019
10−synopsis: The core of an IDE
11−description:
12− A library for building Haskell IDE's on top of the GHC API.
13−homepage: https://github.com/digital-asset/daml#readme
14−bug-reports: https://github.com/digital-asset/daml/issues
15−tested-with: GHC==8.6.5
16−
17−source-repository head
18− type: git
19− location: https://github.com/digital-asset/daml.git
20−
21−library
22− default-language: Haskell2010
23− build-depends:
24− aeson,
25− async,
26− base == 4.*,
27− binary,
28− bytestring,
29− containers,
30− data-default,
31− deepseq,
32− directory,
33− extra,
34− filepath,
35− ghc,
36− ghc-boot-th,
37− ghc-boot,
38− hashable,
39− haskell-lsp,
40− haskell-lsp-types,
41− mtl,
42− network-uri,
43− prettyprinter-ansi-terminal,
44− rope-utf16-splay,
45− shake,
46− sorted-list,
47− stm,
48− syb,
49− text,
50− time,
51− prettyprinter,
52− prettyprinter-ansi-terminal,
53− transformers,
54− unordered-containers,
55− utf8-string
56−
57− cpp-options: -DGHC_STABLE
58− default-extensions:
59− LambdaCase
60− BangPatterns
61− DeriveGeneric
62− RecordWildCards
63− ScopedTypeVariables
64− NamedFieldPuns
65− TupleSections
66− TypeApplications
67− ViewPatterns
68− GeneralizedNewtypeDeriving
69− StandaloneDeriving
70−
71− hs-source-dirs:
72− src
73− exposed-modules:
74− Development.IDE.Core.Compile
75− Development.IDE.Core.FileStore
76− Development.IDE.Core.OfInterest
77− Development.IDE.Core.Rules
78− Development.IDE.Core.RuleTypes
79− Development.IDE.Core.Service
80− Development.IDE.Core.Shake
81− Development.IDE.GHC.Compat
82− Development.IDE.GHC.CPP
83− Development.IDE.GHC.Error
84− Development.IDE.GHC.Orphans
85− Development.IDE.GHC.Util
86− Development.IDE.GHC.Warnings
87− Development.IDE.Import.DependencyInformation
88− Development.IDE.Import.FindImports
89− Development.IDE.LSP.Definition
90− Development.IDE.LSP.Hover
91− Development.IDE.LSP.LanguageServer
92− Development.IDE.LSP.Notifications
93− Development.IDE.LSP.Protocol
94− Development.IDE.LSP.Server
95− Development.IDE.Spans.AtPoint
96− Development.IDE.Spans.Calculate
97− Development.IDE.Spans.Documentation
98− Development.IDE.Spans.Type
99− Development.IDE.Types.Diagnostics
100− Development.IDE.Types.Location
101− Development.IDE.Types.Logger
102− Development.IDE.Types.Options
103−
104−executable hie-core
105− default-language: Haskell2010
106− hs-source-dirs: exe
107− main-is: Main.hs
108− build-depends:
109− base == 4.*,
110− containers,
111− directory,
112− optparse-applicative,
113− hie-bios,
114− shake,
115− data-default,
116− ghc-paths,
117− ghc,
118− extra,
119− filepath,
120− haskell-lsp,
121− text,
122− hie-core
123− other-modules:
124− Arguments
125−
126− default-extensions:
127− TupleSections
128− RecordWildCards
129− ViewPatterns
hie.yamlnew file+12
@@ -0,0 +1,12 @@
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"
img/vscode2.pngnew file

Binary file not shown.

include/ghc-api-version.hnew file+10
@@ -0,0 +1,10 @@
1+#ifndef GHC_API_VERSION_H
2+#define GHC_API_VERSION_H
3+
4+#ifdef GHC_LIB
5+#define MIN_GHC_API_VERSION(x,y,z) MIN_VERSION_ghc_lib(x,y,z)
6+#else
7+#define MIN_GHC_API_VERSION(x,y,z) MIN_VERSION_ghc(x,y,z)
8+#endif
9+
10+#endif
install.batnew file+6
@@ -0,0 +1,6 @@
1+:: Copyright (c) 2019 The DAML Authors. All rights reserved.
2+:: SPDX-License-Identifier: Apache-2.0
3+
4+@REM Install ghcide where cabal install would put it on Windows
5+@REM but avoid checking configure or installing local libraries (faster)
6+ghc Main -o dist\obj\ghcide.exe -XBangPatterns -XDeriveGeneric -XGeneralizedNewtypeDeriving -XLambdaCase -XNamedFieldPuns -XRecordWildCards -XScopedTypeVariables -XStandaloneDeriving -XTupleSections -XTypeApplications -XViewPatterns -package=ghc -DGHC_STABLE -isrc -iexe -outputdir dist\obj && copy dist\obj\ghcide.exe %AppData%\cabal\bin\ghcide.exe
src-ghc810/Development/IDE/GHC/HieAst.hsnew file+1928
@@ -0,0 +1,1928 @@
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.hsnew file+1789
@@ -0,0 +1,1789 @@
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.hsnew file+19
@@ -0,0 +1,19 @@
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+434−270
@@ -1,103 +1,105 @@
1−-- Copyright (c) 2019 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
1+-- Copyright (c) 2019 The DAML Authors. All rights reserved.
2-- SPDX-License-Identifier: Apache-2.0
2-- SPDX-License-Identifier: Apache-2.0
3
3
4−{-# LANGUAGE OverloadedStrings #-}
5{-# LANGUAGE RankNTypes #-}
4{-# LANGUAGE RankNTypes #-}
6{-# LANGUAGE CPP #-}
5{-# LANGUAGE CPP #-}
6+#include "ghc-api-version.h"
7
7
8-- | Based on https://ghc.haskell.org/trac/ghc/wiki/Commentary/Compiler/API.
8-- | Based on https://ghc.haskell.org/trac/ghc/wiki/Commentary/Compiler/API.
9-- Given a list of paths to find libraries, and a file to compile, produce a list of 'CoreModule' values.
9-- Given a list of paths to find libraries, and a file to compile, produce a list of 'CoreModule' values.
10module Development.IDE.Core.Compile
10module Development.IDE.Core.Compile
11 ( TcModuleResult(..)
11 ( TcModuleResult(..)
12− , getGhcDynFlags
12+ , RunSimplifier(..)
13 , compileModule
13 , compileModule
14− , getSrcSpanInfos
15 , parseModule
14 , parseModule
16− , parseFileContents
17 , typecheckModule
15 , typecheckModule
18 , computePackageDeps
16 , computePackageDeps
17+ , addRelativeImport
18+ , mkTcModuleResult
19+ , generateByteCode
20+ , generateAndWriteHieFile
21+ , generateAndWriteHiFile
22+ , getModSummaryFromImports
23+ , loadHieFile
24+ , loadInterface
25+ , loadDepModule
26+ , loadModuleHome
19 ) where
27 ) where
20
28
29+import Development.IDE.Core.RuleTypes
30+import Development.IDE.Core.Preprocessor
31+import Development.IDE.Core.Shake
32+import Development.IDE.GHC.Error
21−import Development.IDE.GHC.Warnings
33+import Development.IDE.GHC.Warnings
22−import Development.IDE.GHC.CPP
23−import Development.IDE.Types.Diagnostics
34+import Development.IDE.Types.Diagnostics
24−import qualified Development.IDE.Import.FindImports as FindImports
25−import Development.IDE.GHC.Error
26−import Development.IDE.Spans.Calculate
27import Development.IDE.GHC.Orphans()
35import Development.IDE.GHC.Orphans()
28import Development.IDE.GHC.Util
36import Development.IDE.GHC.Util
29−import Development.IDE.GHC.Compat
37+import qualified GHC.LanguageExtensions.Type as GHC
30import Development.IDE.Types.Options
38import Development.IDE.Types.Options
31import Development.IDE.Types.Location
39import Development.IDE.Types.Location
40+import Outputable
41+
42+#if MIN_GHC_API_VERSION(8,6,0)
43+import DynamicLoading (initializePlugins)
44+#endif
32
45
33import GHC hiding (parseModule, typecheckModule)
46import GHC hiding (parseModule, typecheckModule)
34import qualified Parser
47import qualified Parser
35import Lexer
48import Lexer
49+#if MIN_GHC_API_VERSION(8,10,0)
50+#else
36import ErrUtils
51import ErrUtils
52+#endif
37
53
38−import qualified GHC
54+import Finder
39−import Panic
55+import qualified Development.IDE.GHC.Compat as GHC
56+import qualified Development.IDE.GHC.Compat as Compat
40import GhcMonad
57import GhcMonad
41import GhcPlugins as GHC hiding (fst3, (<>))
58import GhcPlugins as GHC hiding (fst3, (<>))
42import qualified HeaderInfo as Hdr
59import qualified HeaderInfo as Hdr
60+import HscMain (hscInteractive, hscSimplify)
61+import LoadIface (readIface)
62+import qualified Maybes
43import MkIface
63import MkIface
64+import NameCache
44import StringBuffer as SB
65import StringBuffer as SB
66+import TcRnMonad (initIfaceLoad, tcg_th_coreplugins)
67+import TcIface (typecheckIface)
45import TidyPgm
68import TidyPgm
46−import qualified GHC.LanguageExtensions as LangExt
47
69
48−import Control.DeepSeq
70+import Control.Exception.Safe
71+import Control.Monad.Extra
49−import Control.Monad
72+import Control.Monad.Except
50import Control.Monad.Trans.Except
73import Control.Monad.Trans.Except
74+import Data.Bifunctor (first, second)
51import qualified Data.Text as T
75import qualified Data.Text as T
52import Data.IORef
76import Data.IORef
53import Data.List.Extra
77import Data.List.Extra
54import Data.Maybe
78import Data.Maybe
55−import Data.Tuple.Extra
56import qualified Data.Map.Strict as Map
79import qualified Data.Map.Strict as Map
57−import Development.IDE.Spans.Type
58import System.FilePath
80import System.FilePath
59import System.Directory
81import System.Directory
60−import System.IO.Extra
82+import System.IO.Extra
61−import Data.Char
83+import Data.Either.Extra (maybeToEither)
62−
84+import Control.DeepSeq (rnf)
85+import Control.Exception (evaluate)
86+import Exception (ExceptionMonad)
63
87
64−-- | Contains the typechecked module and the OrigNameCache entry for
65−-- that module.
66−data TcModuleResult = TcModuleResult
67− { tmrModule :: TypecheckedModule
68− , tmrModInfo :: HomeModInfo
69− }
70−instance Show TcModuleResult where
71− show = show . pm_mod_summary . tm_parsed_module . tmrModule
72−
73−instance NFData TcModuleResult where
74− rnf = rwhnf
75
88
76−
77−-- | Get source span info, used for e.g. AtPoint and Goto Definition.
78−getSrcSpanInfos
79− :: ParsedModule
80− -> HscEnv
81− -> [(Located ModuleName, Maybe NormalizedFilePath)]
82− -> TcModuleResult
83− -> IO [SpanInfo]
84−getSrcSpanInfos mod env imports tc =
85− runGhcSession (Just mod) env
86− . getSpanInfo imports
87− $ tmrModule tc
88−
89−
90−-- | Given a string buffer, return a pre-processed @ParsedModule@.
89+-- | Given a string buffer, return the string (after preprocessing) and the 'ParsedModule'.
91parseModule
90parseModule
92 :: IdeOptions
91 :: IdeOptions
93 -> HscEnv
92 -> HscEnv
93+ -> [PackageName]
94 -> FilePath
94 -> FilePath
95 -> Maybe SB.StringBuffer
95 -> Maybe SB.StringBuffer
96− -> IO ([FileDiagnostic], Maybe ParsedModule)
96+ -> IO (IdeResult (StringBuffer, ParsedModule))
97−parseModule IdeOptions{..} env file =
97+parseModule IdeOptions{..} env comp_pkgs filename mbContents =
98− fmap (either (, Nothing) (second Just)) .
98+ fmap (either (, Nothing) id) $
99− -- We need packages since imports fail to resolve otherwise.
99+ evalGhcEnv env $ runExceptT $ do
100+ (contents, dflags) <- preprocessor filename mbContents
100− runGhcSession Nothing env . runExceptT . parseFileContents optPreprocessor file
101+ (diag, modu) <- parseFileContents optPreprocessor dflags comp_pkgs filename contents
102+ return (diag, Just (contents, modu))
101
103
102
104
103-- | Given a package identifier, what packages does it depend on
105-- | Given a package identifier, what packages does it depend on
@@ -108,130 +110,235 @@ computePackageDeps
108computePackageDeps env pkg = do
110computePackageDeps env pkg = do
109 let dflags = hsc_dflags env
111 let dflags = hsc_dflags env
110 case lookupInstalledPackage dflags pkg of
112 case lookupInstalledPackage dflags pkg of
111− Nothing -> return $ Left [ideErrorText (toNormalizedFilePath noFilePath) $
113+ Nothing -> return $ Left [ideErrorText (toNormalizedFilePath' noFilePath) $
112 T.pack $ "unknown package: " ++ show pkg]
114 T.pack $ "unknown package: " ++ show pkg]
113 Just pkgInfo -> return $ Right $ depends pkgInfo
115 Just pkgInfo -> return $ Right $ depends pkgInfo
114
116
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
115
146
116−-- | Typecheck a single module using the supplied dependencies and packages.
147+initPlugins :: GhcMonad m => ModSummary -> m ModSummary
117−typecheckModule
148+initPlugins modSummary = do
118− :: IdeOptions
149+#if MIN_GHC_API_VERSION(8,6,0)
119− -> HscEnv
150+ session <- getSession
120− -> [TcModuleResult]
151+ dflags <- liftIO $ initializePlugins session (ms_hspp_opts modSummary)
121− -> ParsedModule
152+ return modSummary{ms_hspp_opts = dflags}
122− -> IO ([FileDiagnostic], Maybe TcModuleResult)
153+#else
123−typecheckModule opt packageState deps pm =
154+ return modSummary
124− fmap (either (, Nothing) (second Just)) $
155+#endif
125− runGhcSession (Just pm) packageState $
156+
126− catchSrcErrors $ do
157+-- | Whether we should run the -O0 simplifier when generating core.
127− setupEnv deps
158+--
128− (warnings, tcm) <- withWarnings $ \tweak ->
159+-- This is required for template Haskell to work but we disable this in DAML.
129− GHC.typecheckModule pm{pm_mod_summary = tweak $ pm_mod_summary pm}
160+-- See #256
130− tcm2 <- mkTcModuleResult (WriteInterface $ optWriteIface opt) tcm
161+newtype RunSimplifier = RunSimplifier Bool
131− return (warnings, tcm2)
132
162
133-- | Compile a single type-checked module to a 'CoreModule' value, or
163-- | Compile a single type-checked module to a 'CoreModule' value, or
134-- provide errors.
164-- provide errors.
135compileModule
165compileModule
136− :: ParsedModule
166+ :: RunSimplifier
137 -> HscEnv
167 -> HscEnv
138− -> [TcModuleResult]
168+ -> [(ModSummary, HomeModInfo)]
139 -> TcModuleResult
169 -> TcModuleResult
140− -> IO ([FileDiagnostic], Maybe CoreModule)
170+ -> IO (IdeResult (SafeHaskellMode, CgGuts, ModDetails))
141−compileModule mod packageState deps tmr =
171+compileModule (RunSimplifier simplify) packageState deps tmr =
142 fmap (either (, Nothing) (second Just)) $
172 fmap (either (, Nothing) (second Just)) $
143− runGhcSession (Just mod) packageState $
173+ evalGhcEnv packageState $
144− catchSrcErrors $ do
174+ catchSrcErrors "compile" $ do
145− setupEnv (deps ++ [tmr])
175+ setupEnv (deps ++ [(tmrModSummary tmr, tmrModInfo tmr)])
146
176
147 let tm = tmrModule tmr
177 let tm = tmrModule tmr
148 session <- getSession
178 session <- getSession
149− (warnings,desugar) <- withWarnings $ \tweak -> do
179+ (warnings,desugar) <- withWarnings "compile" $ \tweak -> do
150 let pm = tm_parsed_module tm
180 let pm = tm_parsed_module tm
151 let pm' = pm{pm_mod_summary = tweak $ pm_mod_summary pm}
181 let pm' = pm{pm_mod_summary = tweak $ pm_mod_summary pm}
152 let tm' = tm{tm_parsed_module = pm'}
182 let tm' = tm{tm_parsed_module = pm'}
153 GHC.dm_core_module <$> GHC.desugarModule tm'
183 GHC.dm_core_module <$> GHC.desugarModule tm'
154−
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
155 -- give variables unique OccNames
191 -- give variables unique OccNames
156− (tidy, details) <- liftIO $ tidyProgram session desugar
192+ (guts, details) <- liftIO $ tidyProgram session desugared_guts
157−
193+ return (map snd warnings, (mg_safe_haskell desugar, guts, details))
158− let core = CoreModule
159− (cg_module tidy)
160− (md_types details)
161− (cg_binds tidy)
162− (mg_safe_haskell desugar)
163−
164− return (warnings, core)
165−
166
194
167−getGhcDynFlags :: ParsedModule -> HscEnv -> IO DynFlags
195+generateByteCode :: HscEnv -> [(ModSummary, HomeModInfo)] -> TcModuleResult -> CgGuts -> IO (IdeResult Linkable)
168−getGhcDynFlags mod pkg = runGhcSession (Just mod) pkg getSessionDynFlags
196+generateByteCode hscEnv deps tmr guts =
169−
197+ fmap (either (, Nothing) (second Just)) $
170−-- | Evaluate a GHC session using a new environment constructed with
198+ evalGhcEnv hscEnv $
171−-- the supplied options.
199+ catchSrcErrors "bytecode" $ do
172−runGhcSession
200+ setupEnv (deps ++ [(tmrModSummary tmr, tmrModInfo tmr)])
173− :: Maybe ParsedModule
201+ session <- getSession
174− -> HscEnv
202+ (warnings, (_, bytecode, sptEntries)) <- withWarnings "bytecode" $ \tweak ->
175− -> Ghc a
203+#if MIN_GHC_API_VERSION(8,10,0)
176− -> IO a
204+ liftIO $ hscInteractive session guts (GHC.ms_location $ tweak $ GHC.pm_mod_summary $ GHC.tm_parsed_module $ tmrModule tmr)
177−runGhcSession modu env act = runGhcEnv env $ do
205+#else
178− modifyDynFlags $ \x -> x
206+ liftIO $ hscInteractive session guts (tweak $ GHC.pm_mod_summary $ GHC.tm_parsed_module $ tmrModule tmr)
207+#endif
208+ let summary = pm_mod_summary $ tm_parsed_module $ tmrModule tmr
209+ let unlinked = BCOs bytecode sptEntries
210+ let linkable = LM (ms_hs_date summary) (ms_mod summary) [unlinked]
211+ pure (map snd warnings, linkable)
212+
213+demoteTypeErrorsToWarnings :: ParsedModule -> ParsedModule
214+demoteTypeErrorsToWarnings =
215+ (update_pm_mod_summary . update_hspp_opts) demoteTEsToWarns where
216+
217+ demoteTEsToWarns :: DynFlags -> DynFlags
218+ -- convert the errors into warnings, and also check the warnings are enabled
219+ demoteTEsToWarns = (`wopt_set` Opt_WarnDeferredTypeErrors)
220+ . (`wopt_set` Opt_WarnTypedHoles)
221+ . (`wopt_set` Opt_WarnDeferredOutOfScopeVariables)
222+ . (`gopt_set` Opt_DeferTypeErrors)
223+ . (`gopt_set` Opt_DeferTypedHoles)
224+ . (`gopt_set` Opt_DeferOutOfScopeVariables)
225+
226+enableTopLevelWarnings :: ParsedModule -> ParsedModule
227+enableTopLevelWarnings =
228+ (update_pm_mod_summary . update_hspp_opts)
229+ ((`wopt_set` Opt_WarnMissingPatternSynonymSignatures) .
230+ (`wopt_set` Opt_WarnMissingSignatures))
231+ -- the line below would show also warnings for let bindings without signature
232+ -- ((`wopt_set` Opt_WarnMissingSignatures) . (`wopt_set` Opt_WarnMissingLocalSignatures)))
233+
234+update_hspp_opts :: (DynFlags -> DynFlags) -> ModSummary -> ModSummary
235+update_hspp_opts up ms = ms{ms_hspp_opts = up $ ms_hspp_opts ms}
236+
237+update_pm_mod_summary :: (ModSummary -> ModSummary) -> ParsedModule -> ParsedModule
238+update_pm_mod_summary up pm =
239+ pm{pm_mod_summary = up $ pm_mod_summary pm}
240+
241+unDefer :: (WarnReason, FileDiagnostic) -> FileDiagnostic
242+unDefer (Reason Opt_WarnDeferredTypeErrors , fd) = upgradeWarningToError fd
243+unDefer (Reason Opt_WarnTypedHoles , fd) = upgradeWarningToError fd
244+unDefer (Reason Opt_WarnDeferredOutOfScopeVariables, fd) = upgradeWarningToError fd
245+unDefer ( _ , fd) = fd
246+
247+upgradeWarningToError :: FileDiagnostic -> FileDiagnostic
248+upgradeWarningToError (nfp, sh, fd) =
249+ (nfp, sh, fd{_severity = Just DsError, _message = warn2err $ _message fd}) where
250+ warn2err :: T.Text -> T.Text
251+ warn2err = T.intercalate ": error:" . T.splitOn ": warning:"
252+
253+hideDiag :: DynFlags -> (WarnReason, FileDiagnostic) -> (WarnReason, FileDiagnostic)
254+hideDiag originalFlags (Reason warning, (nfp, _sh, fd))
255+ | not (wopt warning originalFlags) = (Reason warning, (nfp, HideDiag, fd))
256+hideDiag _originalFlags t = t
257+
258+addRelativeImport :: NormalizedFilePath -> ModuleName -> DynFlags -> DynFlags
259+addRelativeImport fp modu dflags = dflags
179− {importPaths = nubOrd $ maybeToList (moduleImportPaths =<< modu) ++ importPaths x}
260+ {importPaths = nubOrd $ maybeToList (moduleImportPath fp modu) ++ importPaths dflags}
180− act
181−
182−
183−moduleImportPaths :: GHC.ParsedModule -> Maybe FilePath
184−moduleImportPaths pm
185− | rootModDir == "." = Just rootPathDir
186− | otherwise =
187− -- TODO (MK) stripSuffix (normalise rootModDir) (normalise rootPathDir)
188− -- would be a better choice but at the moment we do not consistently
189− -- normalize file paths in the Shake graph so we can end up with the
190− -- same module being represented twice in the Shake graph.
191− Just $ dropTrailingPathSeparator $ dropEnd (length rootModDir) rootPathDir
192− where
193− ms = GHC.pm_mod_summary pm
194− file = GHC.ms_hspp_file ms
195− mod' = GHC.ms_mod ms
196− rootPathDir = takeDirectory file
197− rootModDir = takeDirectory . moduleNameSlashes . GHC.moduleName $ mod'
198−
199−
200−newtype WriteInterface = WriteInterface Bool
201
261
202mkTcModuleResult
262mkTcModuleResult
203 :: GhcMonad m
263 :: GhcMonad m
204− => WriteInterface
205− -> TypecheckedModule
264+ => TypecheckedModule
206 -> m TcModuleResult
265 -> m TcModuleResult
207−mkTcModuleResult (WriteInterface writeIface) tcm = do
266+mkTcModuleResult tcm = do
208− session <- getSession
267+ 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
209− (iface,_) <- liftIO $ mkIfaceTc session Nothing Sf_None details tcGblEnv
272+ (iface, _) <- liftIO $ mkIfaceTc session Nothing sf details tcGblEnv
210− liftIO $ when writeIface $ do
273+#endif
211− let path = ".interfaces" </> file tcm
212− createDirectoryIfMissing True (takeDirectory path)
213− writeIfaceFile (hsc_dflags session) (replaceExtension path ".hi") iface
214− -- For now, we write .hie files whenever we write .hi files which roughly corresponds to
215− -- when we are building a package. It should be easily decoupable if that turns out to be
216− -- useful.
217− hieFile <- runHsc session $ mkHieFile (tcModSummary tcm) tcGblEnv (fromJust $ renamedSource tcm)
218− writeHieFile (replaceExtension path ".hie") hieFile
219 let mod_info = HomeModInfo iface details Nothing
274 let mod_info = HomeModInfo iface details Nothing
220 return $ TcModuleResult tcm mod_info
275 return $ TcModuleResult tcm mod_info
221 where
276 where
222− file = ms_hspp_file . tcModSummary
223 (tcGblEnv, details) = tm_internals_ tcm
277 (tcGblEnv, details) = tm_internals_ tcm
224
278
225−tcModSummary :: TypecheckedModule -> ModSummary
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
226−tcModSummary = pm_mod_summary . tm_parsed_module
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+
227
324
228-- | Setup the environment that GHC needs according to our
325-- | Setup the environment that GHC needs according to our
229-- best understanding (!)
326-- best understanding (!)
327+--
328+-- This involves setting up the finder cache and populating the
329+-- HPT.
230−setupEnv :: GhcMonad m => [TcModuleResult] -> m ()
330+setupEnv :: GhcMonad m => [(ModSummary, HomeModInfo)] -> m ()
231setupEnv tms = do
331setupEnv tms = do
232− session <- getSession
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
233
336
234− let mss = map (pm_mod_summary . tm_parsed_module . tmrModule) tms
337+-- | Initialise the finder cache, dependencies should be topologically
338+-- sorted.
339+setupFinderCache :: GhcMonad m => [ModSummary] -> m ()
340+setupFinderCache mss = do
341+ session <- getSession
235
342
236 -- set the target and module graph in the session
343 -- set the target and module graph in the session
237 let graph = mkModuleGraph mss
344 let graph = mkModuleGraph mss
@@ -241,139 +348,171 @@ setupEnv tms = do
241 -- by putting them in the finder cache.
348 -- by putting them in the finder cache.
242 let ims = map (InstalledModule (thisInstalledUnitId $ hsc_dflags session) . moduleName . ms_mod) mss
349 let ims = map (InstalledModule (thisInstalledUnitId $ hsc_dflags session) . moduleName . ms_mod) mss
243 ifrs = zipWith (\ms -> InstalledFound (ms_location ms)) mss ims
350 ifrs = zipWith (\ms -> InstalledFound (ms_location ms)) mss ims
351+ -- We have to create a new IORef here instead of modifying the existing IORef as
352+ -- it is shared between concurrent compilations.
244− liftIO $ modifyIORef (hsc_FC session) $ \fc ->
353+ prevFinderCache <- liftIO $ readIORef $ hsc_FC session
354+ let newFinderCache =
355+ foldl'
245− foldl' (\fc (im, ifr) -> GHC.extendInstalledModuleEnv fc im ifr) fc
356+ (\fc (im, ifr) -> GHC.extendInstalledModuleEnv fc im ifr) prevFinderCache
246− $ zip ims ifrs
357+ $ zip ims ifrs
247−
358+ newFinderCacheVar <- liftIO $ newIORef $! newFinderCache
248− -- load dependent modules, which must be in topological order.
359+ modifySession $ \s -> s { hsc_FC = newFinderCacheVar }
249− mapM_ loadModuleHome tms
250
360
251
361
252-- | Load a module, quickly. Input doesn't need to be desugared.
362-- | Load a module, quickly. Input doesn't need to be desugared.
253-- A module must be loaded before dependent modules can be typechecked.
363-- A module must be loaded before dependent modules can be typechecked.
254-- This variant of loadModuleHome will *never* cause recompilation, it just
364-- This variant of loadModuleHome will *never* cause recompilation, it just
255-- modifies the session.
365-- 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.
256loadModuleHome
370loadModuleHome
257− :: (GhcMonad m)
371+ :: HomeModInfo
258− => TcModuleResult
372+ -> HscEnv
259− -> m ()
373+ -> HscEnv
260−loadModuleHome tmr = modifySession $ \e ->
374+loadModuleHome mod_info e =
261− e { hsc_HPT = addToHpt (hsc_HPT e) mod mod_info }
375+ e { hsc_HPT = addToHpt (hsc_HPT e) mod_name mod_info }
262− where
376+ where
263− ms = pm_mod_summary . tm_parsed_module . tmrModule $ tmr
377+ mod_name = moduleName $ mi_module $ hm_iface mod_info
264− mod_info = tmrModInfo tmr
378+
265− mod = ms_mod_name ms
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
389+
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'
395+
396+-- | GhcMonad function to chase imports of a module given as a StringBuffer. Returns given module's
397+-- name and its imports.
398+getImportsParsed :: DynFlags ->
399+ GHC.ParsedSource ->
400+ Either [FileDiagnostic] (GHC.ModuleName, [(Bool, (Maybe FastString, Located GHC.ModuleName))])
401+getImportsParsed dflags (L loc parsed) = do
402+ let modName = maybe (GHC.mkModuleName "Main") GHC.unLoc $ GHC.hsmodName parsed
403+
404+ -- most of these corner cases are also present in https://hackage.haskell.org/package/ghc-8.6.1/docs/src/HeaderInfo.html#getImports
405+ -- but we want to avoid parsing the module twice
406+ let implicit_prelude = xopt GHC.ImplicitPrelude dflags
407+ implicit_imports = Hdr.mkPrelImports modName loc implicit_prelude $ GHC.hsmodImports parsed
408+
409+ -- filter out imports that come from packages
410+ return (modName, [(ideclSource i, (fmap sl_fs $ ideclPkgQual i, ideclName i))
411+ | i <- map GHC.unLoc $ implicit_imports ++ GHC.hsmodImports parsed
412+ , GHC.moduleNameString (GHC.unLoc $ ideclName i) /= "GHC.Prim"
413+ ])
266
414
267-- | Produce a module summary from a StringBuffer.
415-- | Produce a module summary from a StringBuffer.
268getModSummaryFromBuffer
416getModSummaryFromBuffer
269 :: GhcMonad m
417 :: GhcMonad m
270 => FilePath
418 => FilePath
271− -> SB.StringBuffer
272 -> DynFlags
419 -> DynFlags
273 -> GHC.ParsedSource
420 -> GHC.ParsedSource
274 -> ExceptT [FileDiagnostic] m ModSummary
421 -> ExceptT [FileDiagnostic] m ModSummary
275−getModSummaryFromBuffer fp contents dflags parsed = do
422+getModSummaryFromBuffer fp dflags parsed = do
276− (modName, imports) <- FindImports.getImportsParsed dflags parsed
423+ (modName, imports) <- liftEither $ getImportsParsed dflags parsed
277−
424+
278− let modLoc = ModLocation
425+ modLoc <- liftIO $ mkHomeModLocation dflags modName fp
279− { ml_hs_file = Just fp
280− , ml_hi_file = replaceExtension fp "hi"
281− , ml_obj_file = replaceExtension fp "o"
282−#ifndef GHC_STABLE
283− , ml_hie_file = replaceExtension fp "hie"
284−#endif
285− -- This does not consider the dflags configuration
286− -- (-osuf and -hisuf, object and hi dir.s).
287− -- However, we anyway don't want to generate them.
288− }
289− InstalledUnitId unitId = thisInstalledUnitId dflags
426+ let InstalledUnitId unitId = thisInstalledUnitId dflags
290 return $ ModSummary
427 return $ ModSummary
291 { ms_mod = mkModule (fsToUnitId unitId) modName
428 { ms_mod = mkModule (fsToUnitId unitId) modName
292 , ms_location = modLoc
429 , ms_location = modLoc
293 , ms_hs_date = error "Rules should not depend on ms_hs_date"
430 , ms_hs_date = error "Rules should not depend on ms_hs_date"
294− -- ^ When we are working with a virtual file we do not have a file date.
431+ -- When we are working with a virtual file we do not have a file date.
295− -- To avoid silent issues where something is not processed because the date
432+ -- To avoid silent issues where something is not processed because the date
296− -- has not changed, we make sure that things blow up if they depend on the
433+ -- has not changed, we make sure that things blow up if they depend on the
297− -- date.
434+ -- date.
298− , ms_textual_imps = imports
435+ , ms_textual_imps = [imp | (False, imp) <- imports]
299 , ms_hspp_file = fp
436 , ms_hspp_file = fp
300 , ms_hspp_opts = dflags
437 , ms_hspp_opts = dflags
301− , ms_hspp_buf = Just contents
438+ , ms_hspp_buf = Nothing
302
439
303 -- defaults:
440 -- defaults:
304− , ms_hsc_src = HsSrcFile
441+ , ms_hsc_src = sourceType
305 , ms_obj_date = Nothing
442 , ms_obj_date = Nothing
306 , ms_iface_date = Nothing
443 , ms_iface_date = Nothing
307−#ifndef GHC_STABLE
444+#if MIN_GHC_API_VERSION(8,8,0)
308 , ms_hie_date = Nothing
445 , ms_hie_date = Nothing
309#endif
446#endif
310− , ms_srcimps = [] -- source imports are not allowed
447+ , ms_srcimps = [imp | (True, imp) <- imports]
311 , ms_parsed_mod = Nothing
448 , ms_parsed_mod = Nothing
312 }
449 }
313−
450+ where
314−-- | Run CPP on a file
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
315−runCpp :: DynFlags -> FilePath -> Maybe SB.StringBuffer -> IO SB.StringBuffer
458+ -> Maybe SB.StringBuffer
316−runCpp dflags filename contents = withTempDir $ \dir -> do
459+ -> ExceptT [FileDiagnostic] m ModSummary
317− let out = dir </> takeFileName filename <.> "out"
460+getModSummaryFromImports fp contents = do
318− case contents of
461+ (contents, dflags) <- preprocessor fp contents
319− Nothing -> do
462+ (srcImports, textualImports, L _ moduleName) <-
320− -- Happy case, file is not modified, so run CPP on it in-place
463+ ExceptT $ liftIO $ first (diagFromErrMsgs "parser" dflags) <$> GHC.getHeaderImports dflags contents fp fp
321− -- which also makes things like relative #include files work
464+
322− -- and means location information is correct
465+ -- Force bits that might keep the string buffer and DynFlags alive unnecessarily
323− doCpp dflags True filename out
466+ liftIO $ evaluate $ rnf srcImports
324− liftIO $ SB.hGetStringBuffer out
467+ liftIO $ evaluate $ rnf textualImports
325−
468+
326− Just contents -> do
469+ modLoc <- liftIO $ mkHomeModLocation dflags moduleName fp
327− -- Sad path, we have to create a version of the path in a temp dir
470+
328− -- __FILE__ macro is wrong, ignoring that for now (likely not a real issue)
471+ let mod = mkModule (thisPackage dflags) moduleName
329−
472+ sourceType = if "-boot" `isSuffixOf` takeExtension fp then HsBootFile else HsSrcFile
330− -- Relative includes aren't going to work, so we fix that by adding to the include path.
473+ summary =
331− let addSelf (IncludeSpecs quote global) = IncludeSpecs (takeDirectory filename : quote) global
474+ ModSummary
332− dflags <- return dflags{includePaths = addSelf $ includePaths dflags}
475+ { ms_mod = mod
333−
476+#if MIN_GHC_API_VERSION(8,8,0)
334− -- Location information is wrong, so we fix that by patching it afterwards.
477+ , ms_hie_date = Nothing
335− let inp = dir </> "___HIE_CORE_MAGIC___"
478+#endif
336− withBinaryFile inp WriteMode $ \h ->
479+ , ms_hs_date = error "Rules should not depend on ms_hs_date"
337− hPutStringBuffer h contents
480+ -- When we are working with a virtual file we do not have a file date.
338− doCpp dflags True inp out
481+ -- To avoid silent issues where something is not processed because the date
339−
482+ -- has not changed, we make sure that things blow up if they depend on the date.
340− -- Fix up the filename in lines like:
483+ , ms_hsc_src = sourceType
341− -- # 1 "C:/Temp/extra-dir-914611385186/___HIE_CORE_MAGIC___"
484+ , ms_hspp_buf = Nothing
342− let tweak x
485+ , ms_hspp_file = fp
343− | Just x <- stripPrefix "# " x
486+ , ms_hspp_opts = dflags
344− , "___HIE_CORE_MAGIC___" `isInfixOf` x
487+ , ms_iface_date = Nothing
345− , let num = takeWhile (not . isSpace) x
488+ , ms_location = modLoc
346− -- important to use /, and never \ for paths, even on Windows, since then C escapes them
489+ , ms_obj_date = Nothing
347− -- and GHC gets all confused
490+ , ms_parsed_mod = Nothing
348− = "# " <> num <> " \"" <> map (\x -> if isPathSeparator x then '/' else x) filename <> "\""
491+ , ms_srcimps = srcImports
349− | otherwise = x
492+ , ms_textual_imps = textualImports
350− stringToStringBuffer . unlines . map tweak . lines <$> readFileUTF8' out
493+ }
351−
494+ return summary
352−
495+
496+
353−-- | Given a buffer, flags, file path and module summary, produce a
497+-- | Given a buffer, flags, and file path, produce a
354−-- parsed module (or errors) and any parse warnings.
498+-- parsed module (or errors) and any parse warnings. Does not run any preprocessors
355parseFileContents
499parseFileContents
356 :: GhcMonad m
500 :: GhcMonad m
357− => (GHC.ParsedSource -> ([(GHC.SrcSpan, String)], GHC.ParsedSource))
501+ => (GHC.ParsedSource -> IdePreprocessedSource)
502+ -> DynFlags -- ^ flags to use
503+ -> [PackageName] -- ^ The package imports to ignore
358 -> FilePath -- ^ the filename (for source locations)
504 -> FilePath -- ^ the filename (for source locations)
359− -> Maybe SB.StringBuffer -- ^ Haskell module source text (full Unicode is supported)
505+ -> SB.StringBuffer -- ^ Haskell module source text (full Unicode is supported)
360 -> ExceptT [FileDiagnostic] m ([FileDiagnostic], ParsedModule)
506 -> ExceptT [FileDiagnostic] m ([FileDiagnostic], ParsedModule)
361−parseFileContents preprocessor filename mbContents = do
507+parseFileContents customPreprocessor dflags comp_pkgs filename contents = do
362− contents <- liftIO $ maybe (hGetStringBuffer filename) return mbContents
363 let loc = mkRealSrcLoc (mkFastString filename) 1 1
508 let loc = mkRealSrcLoc (mkFastString filename) 1 1
364− dflags <- ExceptT $ parsePragmasIntoDynFlags filename contents
365−
366− (contents, dflags) <-
367− if not $ xopt LangExt.Cpp dflags then
368− return (contents, dflags)
369− else do
370− contents <- liftIO $ runCpp dflags filename mbContents
371− dflags <- ExceptT $ parsePragmasIntoDynFlags filename contents
372− return (contents, dflags)
373−
374 case unP Parser.parseModule (mkPState dflags contents loc) of
509 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
375 PFailed _ locErr msgErr ->
513 PFailed _ locErr msgErr ->
376− throwE $ diagFromErrMsg dflags $ mkPlainErrMsg dflags locErr msgErr
514+ throwE $ diagFromErrMsg "parser" dflags $ mkPlainErrMsg dflags locErr msgErr
515+#endif
377 POk pst rdr_module ->
516 POk pst rdr_module ->
378 let hpm_annotations =
517 let hpm_annotations =
379 (Map.fromListWith (++) $ annotations pst,
518 (Map.fromListWith (++) $ annotations pst,
@@ -392,43 +531,68 @@ parseFileContents preprocessor filename mbContents = do
392 -- errors are those from which a parse tree just can't
531 -- errors are those from which a parse tree just can't
393 -- be produced.
532 -- be produced.
394 unless (null errs) $
533 unless (null errs) $
395− throwE $ diagFromErrMsgs dflags $ snd $ getMessages pst dflags
534+ throwE $ diagFromErrMsgs "parser" dflags $ snd $ getMessages pst dflags
396
535
397 -- Ok, we got here. It's safe to continue.
536 -- Ok, we got here. It's safe to continue.
398− let (errs, parsed) = preprocessor rdr_module
537+ let IdePreprocessedSource preproc_warns errs parsed = customPreprocessor rdr_module
399− unless (null errs) $ throwE $ diagFromStrings errs
538+ unless (null errs) $ throwE $ diagFromStrings "parser" DsError errs
539+ let parsed' = removePackageImports comp_pkgs parsed
540+ let preproc_warnings = diagFromStrings "parser" DsWarning preproc_warns
400− ms <- getModSummaryFromBuffer filename contents dflags parsed
541+ ms <- getModSummaryFromBuffer filename dflags parsed'
401 let pm =
542 let pm =
402 ParsedModule {
543 ParsedModule {
403 pm_mod_summary = ms
544 pm_mod_summary = ms
404− , pm_parsed_source = parsed
545+ , pm_parsed_source = parsed'
405 , pm_extra_src_files=[] -- src imports not allowed
546 , pm_extra_src_files=[] -- src imports not allowed
406 , pm_annotations = hpm_annotations
547 , pm_annotations = hpm_annotations
407 }
548 }
408− warnings = diagFromErrMsgs dflags warns
549+ warnings = diagFromErrMsgs "parser" dflags warns
409− pure (warnings, pm)
550+ pure (warnings ++ preproc_warnings, pm)
410
551
411
552
412−-- | This reads the pragma information directly from the provided buffer.
553+-- | After parsing the module remove all package imports referring to
413−parsePragmasIntoDynFlags
554+-- these packages as we have already dealt with what they map to.
414− :: GhcMonad m
555+removePackageImports :: [PackageName] -> GHC.ParsedSource -> GHC.ParsedSource
415− => FilePath
556+removePackageImports pkgs (L l h@HsModule {hsmodImports} ) = L l (h { hsmodImports = imports' })
416− -> SB.StringBuffer
557+ where
417− -> m (Either [FileDiagnostic] DynFlags)
558+ imports' = map do_one_import hsmodImports
418−parsePragmasIntoDynFlags fp contents = catchSrcErrors $ do
559+ do_one_import (L l i@ImportDecl{ideclPkgQual}) =
419− dflags0 <- getSessionDynFlags
560+ case PackageName . sl_fs <$> ideclPkgQual of
420− let opts = Hdr.getOptions dflags0 contents fp
561+ Just pn | pn `elem` pkgs -> L l (i { ideclPkgQual = Nothing })
421− (dflags, _, _) <- parseDynamicFilePragma dflags0 opts
562+ _ -> L l i
422− return dflags
563+#if MIN_GHC_API_VERSION(8,6,0)
423−
564+ do_one_import l = l
424−-- | Run something in a Ghc monad and catch the errors (SourceErrors and
565+#endif
425−-- compiler-internal exceptions like Panic or InstallationError).
566+
426−catchSrcErrors :: GhcMonad m => m a -> m (Either [FileDiagnostic] a)
567+loadHieFile :: FilePath -> IO GHC.HieFile
427−catchSrcErrors ghcM = do
568+loadHieFile f = do
428− dflags <- getDynFlags
569+ u <- mkSplitUniqSupply 'a'
429− handleGhcException (ghcExceptionToDiagnostics dflags) $
570+ let nameCache = initNameCache u []
430− handleSourceError (sourceErrorToDiagnostics dflags) $
571+ fmap (GHC.hie_file_result . fst) $ GHC.readHieFile nameCache f
431− Right <$> ghcM
572+
432− where
573+-- | Retuns an up-to-date module interface if available.
433− ghcExceptionToDiagnostics dflags = return . Left . diagFromGhcException dflags
574+-- Assumes file exists.
434− sourceErrorToDiagnostics dflags = return . Left . diagFromErrMsgs dflags . srcErrorMessages
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/Core/Debouncer.hsnew file+52
@@ -0,0 +1,52 @@
1+-- Copyright (c) 2019 The DAML Authors. All rights reserved.
2+-- SPDX-License-Identifier: Apache-2.0
3+
4+module Development.IDE.Core.Debouncer
5+ ( Debouncer
6+ , registerEvent
7+ , newAsyncDebouncer
8+ , noopDebouncer
9+ ) where
10+
11+import Control.Concurrent.Extra
12+import Control.Concurrent.Async
13+import Control.Exception
14+import Control.Monad.Extra
15+import Data.Hashable
16+import Data.HashMap.Strict (HashMap)
17+import qualified Data.HashMap.Strict as Map
18+import System.Time.Extra
19+
20+-- | A debouncer can be used to avoid triggering many events
21+-- (e.g. diagnostics) for the same key (e.g. the same file)
22+-- within a short timeframe. This is accomplished
23+-- by delaying each event for a given time. If another event
24+-- is registered for the same key within that timeframe,
25+-- 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 () }
30+
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
34+
35+-- | Register an event that will fire after the given delay if no other event
36+-- for the same key gets registered until then.
37+--
38+-- If there is a pending event for the same key, the pending event will be killed.
39+-- Events are run unmasked so it is up to the user of `registerEvent`
40+-- 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
43+ whenJust (Map.lookup k m) cancel
44+ a <- asyncWithUnmask $ \unmask -> unmask $ do
45+ sleep delay
46+ fire
47+ modifyVar_ d (pure . Map.delete k)
48+ 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.hsnew file+181
@@ -0,0 +1,181 @@
1+{-# LANGUAGE OverloadedLists #-}
2+{-# LANGUAGE TypeFamilies #-}
3+{-# LANGUAGE UndecidableInstances #-}
4+module Development.IDE.Core.FileExists
5+ ( fileExistsRules
6+ , modifyFileExists
7+ , getFileExists
8+ )
9+where
10+
11+import Control.Concurrent.Extra
12+import Control.Exception
13+import Control.Monad.Extra
14+import qualified Data.Aeson as A
15+import Data.Binary
16+import qualified Data.ByteString as BS
17+import Data.HashMap.Strict (HashMap)
18+import qualified Data.HashMap.Strict as HashMap
19+import Data.Maybe
20+import qualified Data.Text as T
21+import Development.IDE.Core.FileStore
22+import Development.IDE.Core.IdeConfiguration
23+import Development.IDE.Core.Shake
24+import Development.IDE.Types.Location
25+import Development.IDE.Types.Logger
26+import Development.Shake
27+import Development.Shake.Classes
28+import GHC.Generics
29+import Language.Haskell.LSP.Messages
30+import Language.Haskell.LSP.Types
31+import Language.Haskell.LSP.Types.Capabilities
32+import qualified System.Directory as Dir
33+
34+-- | A map for tracking the file existence
35+type FileExistsMap = (HashMap NormalizedFilePath Bool)
36+
37+-- | A wrapper around a mutable 'FileExistsMap'
38+newtype FileExistsMapVar = FileExistsMapVar (Var FileExistsMap)
39+
40+instance IsIdeGlobal FileExistsMapVar
41+
42+-- | Grab the current global value of 'FileExistsMap' without acquiring a dependency
43+getFileExistsMapUntracked :: Action FileExistsMap
44+getFileExistsMapUntracked = do
45+ FileExistsMapVar v <- getIdeGlobalAction
46+ liftIO $ readVar v
47+
48+-- | Modify the global store of file exists
49+modifyFileExistsAction :: (FileExistsMap -> IO FileExistsMap) -> Action ()
50+modifyFileExistsAction f = do
51+ FileExistsMapVar var <- getIdeGlobalAction
52+ liftIO $ modifyVar_ var f
53+
54+-- | Modify the global store of file exists
55+modifyFileExists :: IdeState -> [(NormalizedFilePath, Bool)] -> IO ()
56+modifyFileExists state changes = do
57+ FileExistsMapVar var <- getIdeGlobalState state
58+ changesMap <- evaluate $ HashMap.fromList changes
59+
60+ -- Masked to ensure that the previous values are flushed together with the map update
61+ mask $ \_ -> do
62+ -- update the map
63+ modifyVar_ var $ evaluate . HashMap.union changesMap
64+ -- flush previous values
65+ mapM_ (deleteValue state GetFileExists . fst) changes
66+
67+-------------------------------------------------------------------------------------
68+
69+type instance RuleResult GetFileExists = Bool
70+
71+data GetFileExists = GetFileExists
72+ deriving (Eq, Show, Typeable, Generic)
73+
74+instance NFData GetFileExists
75+instance Hashable GetFileExists
76+instance Binary GetFileExists
77+
78+-- | Returns True if the file exists
79+-- Note that a file is not considered to exist unless it is saved to disk.
80+-- In particular, VFS existence is not enough.
81+-- Consider the following example:
82+-- 1. The file @A.hs@ containing the line @import B@ is added to the files of interest
83+-- Since @B.hs@ is neither open nor exists, GetLocatedImports finds Nothing
84+-- 2. The editor creates a new buffer @B.hs@
85+-- Unless the editor also sends a @DidChangeWatchedFile@ event, ghcide will not pick it up
86+-- Most editors, e.g. VSCode, only send the event when the file is saved to disk.
87+getFileExists :: NormalizedFilePath -> Action Bool
88+getFileExists fp = use_ GetFileExists fp
89+
90+-- | Installs the 'getFileExists' rules.
91+-- Provides a fast implementation if client supports dynamic watched files.
92+-- Creates a global state as a side effect in that case.
93+fileExistsRules :: 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
108+
109+-- Requires an lsp client that provides WatchedFiles notifications.
110+fileExistsRulesFast :: IO LspId -> VFSHandle -> Rules ()
111+fileExistsRulesFast getLspId vfs =
112+ 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
120+ fileExistsMap <- getFileExistsMapUntracked
121+ let mbFilesWatched = HashMap.lookup file fileExistsMap
122+ case mbFilesWatched of
123+ Just fv -> pure (summarizeExists fv, ([], Just fv))
124+ Nothing -> do
125+ exist <- liftIO $ getFileExistsVFS vfs file
126+ ShakeExtras { eventer } <- getShakeExtras
127+
128+ -- add a listener for VFS Create/Delete file events,
129+ -- taking the FileExistsMap lock to prevent race conditions
130+ -- that would lead to multiple listeners for the same path
131+ modifyFileExistsAction $ \x -> do
132+ case HashMap.alterF (,Just exist) file x of
133+ (Nothing, x') -> do
134+ -- if the listener addition fails, we never recover. This is a bug.
135+ addListener eventer file
136+ return x'
137+ (Just _, _) ->
138+ -- if the key was already there, do nothing
139+ return x
140+
141+ pure (summarizeExists exist, ([], Just exist))
142+ where
143+ addListener eventer fp = do
144+ reqId <- getLspId
145+ let
146+ req = RequestMessage "2.0" reqId ClientRegisterCapability regParams
147+ fpAsId = T.pack $ fromNormalizedFilePath fp
148+ regParams = RegistrationParams (List [registration])
149+ registration = Registration fpAsId
150+ WorkspaceDidChangeWatchedFiles
151+ (Just (A.toJSON regOptions))
152+ 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
157+ }
158+
159+ eventer $ ReqRegisterCapability req
160+
161+summarizeExists :: Bool -> Maybe BS.ByteString
162+summarizeExists x = Just $ if x then BS.singleton 1 else BS.empty
163+
164+fileExistsRulesSlow:: 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
170+ alwaysRerun
171+ exist <- liftIO $ getFileExistsVFS vfs file
172+ pure (summarizeExists exist, ([], Just exist))
173+
174+getFileExistsVFS :: VFSHandle -> NormalizedFilePath -> IO Bool
175+getFileExistsVFS vfs file = do
176+ -- we deliberately and intentionally wrap the file as an FilePath WITHOUT mkAbsolute
177+ -- so that if the file doesn't exist, is on a shared drive that is unmounted etc we get a properly
178+ -- cached 'No' rather than an exception in the wrong place
179+ handle (\(_ :: IOException) -> return False) $
180+ (isJust <$> getVirtualFile vfs (filePathToUri' file)) ||^
181+ Dir.doesFileExist (fromNormalizedFilePath file)
src/Development/IDE/Core/FileStore.hs+60−53
@@ -1,32 +1,28 @@
1−-- Copyright (c) 2019 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
1+-- Copyright (c) 2019 The DAML Authors. All rights reserved.
2-- SPDX-License-Identifier: Apache-2.0
2-- SPDX-License-Identifier: Apache-2.0
3−
3+{-# LANGUAGE CPP #-}
4{-# LANGUAGE TypeFamilies #-}
4{-# LANGUAGE TypeFamilies #-}
5
5
6module Development.IDE.Core.FileStore(
6module Development.IDE.Core.FileStore(
7− getFileExists, getFileContents,
7+ getFileContents,
8+ getVirtualFile,
8 setBufferModified,
9 setBufferModified,
9 setSomethingModified,
10 setSomethingModified,
10 fileStoreRules,
11 fileStoreRules,
11 VFSHandle,
12 VFSHandle,
12 makeVFSHandle,
13 makeVFSHandle,
13− makeLSPVFSHandle,
14+ makeLSPVFSHandle
14 ) where
15 ) where
15
16
16−import StringBuffer
17import Development.IDE.GHC.Orphans()
17import Development.IDE.GHC.Orphans()
18−import Development.IDE.GHC.Util
18+import Development.IDE.Core.Shake
19−
20import Control.Concurrent.Extra
19import Control.Concurrent.Extra
21import qualified Data.Map.Strict as Map
20import qualified Data.Map.Strict as Map
22import Data.Maybe
21import Data.Maybe
23import qualified Data.Text as T
22import qualified Data.Text as T
24−import Data.Time.Clock
25import Control.Monad.Extra
23import Control.Monad.Extra
26−import qualified System.Directory as Dir
27import Development.Shake
24import Development.Shake
28import Development.Shake.Classes
25import Development.Shake.Classes
29−import Development.IDE.Core.Shake
30import Control.Exception
26import Control.Exception
31import GHC.Generics
27import GHC.Generics
32import Data.Either.Extra
28import Data.Either.Extra
@@ -35,7 +31,18 @@ import qualified Data.ByteString.Char8 as BS
35import Development.IDE.Types.Diagnostics
31import Development.IDE.Types.Diagnostics
36import Development.IDE.Types.Location
32import Development.IDE.Types.Location
37import qualified Data.Rope.UTF16 as Rope
33import qualified Data.Rope.UTF16 as Rope
34+
35+#ifdef mingw32_HOST_OS
38−import Data.Time
36+import Data.Time
37+import qualified System.Directory as Dir
38+#else
39+import Foreign.Ptr
40+import Foreign.C.String
41+import Foreign.C.Types
42+import Foreign.Marshal (alloca)
43+import Foreign.Storable
44+import qualified System.Posix.Error as Posix
45+#endif
39
46
40import Language.Haskell.LSP.Core
47import Language.Haskell.LSP.Core
41import Language.Haskell.LSP.VFS
48import Language.Haskell.LSP.VFS
@@ -64,7 +71,8 @@ makeVFSHandle = do
64 modifyVar_ vfsVar $ \(nextVersion, vfs) -> pure $ (nextVersion + 1, ) $
71 modifyVar_ vfsVar $ \(nextVersion, vfs) -> pure $ (nextVersion + 1, ) $
65 case content of
72 case content of
66 Nothing -> Map.delete uri vfs
73 Nothing -> Map.delete uri vfs
74+ -- The second version number is only used in persistFileVFS which we do not use so we set it to 0.
67− Just content -> Map.insert uri (VirtualFile nextVersion (Rope.fromText content) Nothing) vfs
75+ Just content -> Map.insert uri (VirtualFile nextVersion 0 (Rope.fromText content)) vfs
68 }
76 }
69
77
70makeLSPVFSHandle :: LspFuncs c -> VFSHandle
78makeLSPVFSHandle :: LspFuncs c -> VFSHandle
@@ -75,51 +83,59 @@ makeLSPVFSHandle lspFuncs = VFSHandle
75
83
76
84
77-- | Get the contents of a file, either dirty (if the buffer is modified) or Nothing to mean use from disk.
85-- | Get the contents of a file, either dirty (if the buffer is modified) or Nothing to mean use from disk.
78−type instance RuleResult GetFileContents = (FileVersion, Maybe StringBuffer)
86+type instance RuleResult GetFileContents = (FileVersion, Maybe T.Text)
79−
80−-- | Does the file exist.
81−type instance RuleResult GetFileExists = Bool
82−
83−
84−data GetFileExists = GetFileExists
85− deriving (Eq, Show, Generic)
86−instance Hashable GetFileExists
87−instance NFData GetFileExists
88
87
89data GetFileContents = GetFileContents
88data GetFileContents = GetFileContents
90 deriving (Eq, Show, Generic)
89 deriving (Eq, Show, Generic)
91instance Hashable GetFileContents
90instance Hashable GetFileContents
92instance NFData GetFileContents
91instance NFData GetFileContents
93−
92+instance Binary GetFileContents
94−
95−getFileExistsRule :: VFSHandle -> Rules ()
96−getFileExistsRule vfs =
97− defineEarlyCutoff $ \GetFileExists file -> do
98− alwaysRerun
99− res <- liftIO $ handle (\(_ :: IOException) -> return False) $
100− (isJust <$> getVirtualFile vfs (filePathToUri' file)) ||^
101− Dir.doesFileExist (fromNormalizedFilePath file)
102− return (Just $ if res then BS.singleton '1' else BS.empty, ([], Just res))
103−
104−
105−showTimePrecise :: UTCTime -> String
106−showTimePrecise UTCTime{..} = show (toModifiedJulianDay utctDay, diffTimeToPicoseconds utctDayTime)
107
93
108getModificationTimeRule :: VFSHandle -> Rules ()
94getModificationTimeRule :: VFSHandle -> Rules ()
109getModificationTimeRule vfs =
95getModificationTimeRule vfs =
110 defineEarlyCutoff $ \GetModificationTime file -> do
96 defineEarlyCutoff $ \GetModificationTime file -> do
111 let file' = fromNormalizedFilePath file
97 let file' = fromNormalizedFilePath file
112− let wrap time = (Just $ BS.pack $ showTimePrecise time, ([], Just $ ModificationTime time))
98+ let wrap time@(l,s) = (Just $ BS.pack $ show time, ([], Just $ ModificationTime l s))
113 alwaysRerun
99 alwaysRerun
114 mbVirtual <- liftIO $ getVirtualFile vfs $ filePathToUri' file
100 mbVirtual <- liftIO $ getVirtualFile vfs $ filePathToUri' file
115 case mbVirtual of
101 case mbVirtual of
102+ Just (virtualFileVersion -> ver) ->
116− Just (VirtualFile ver _ _) -> pure (Just $ BS.pack $ show ver, ([], Just $ VFSVersion ver))
103+ pure (Just $ BS.pack $ show ver, ([], Just $ VFSVersion ver))
117− Nothing -> liftIO $ fmap wrap (Dir.getModificationTime file')
104+ Nothing -> liftIO $ fmap wrap (getModTime file')
118 `catch` \(e :: IOException) -> do
105 `catch` \(e :: IOException) -> do
119 let err | isDoesNotExistError e = "File does not exist: " ++ file'
106 let err | isDoesNotExistError e = "File does not exist: " ++ file'
120 | otherwise = "IO error while reading " ++ file' ++ ", " ++ displayException e
107 | otherwise = "IO error while reading " ++ file' ++ ", " ++ displayException e
121 return (Nothing, ([ideErrorText file $ T.pack err], Nothing))
108 return (Nothing, ([ideErrorText file $ T.pack err], Nothing))
122−
109+ where
110+ -- Dir.getModificationTime is surprisingly slow since it performs
111+ -- a ton of conversions. Since we do not actually care about
112+ -- the format of the time, we can get away with something cheaper.
113+ -- For now, we only try to do this on Unix systems where it seems to get the
114+ -- time spent checking file modifications (which happens on every change)
115+ -- from > 0.5s to ~0.15s.
116+ -- We might also want to try speeding this up on Windows at some point.
117+ -- TODO leverage DidChangeWatchedFile lsp notifications on clients that
118+ -- support them, as done for GetFileExists
119+ getModTime :: FilePath -> IO (Int,Int)
120+ getModTime f =
121+#ifdef mingw32_HOST_OS
122+ do time <- Dir.getModificationTime f
123+ let !day = fromInteger $ toModifiedJulianDay $ utctDay time
124+ !dayTime = fromInteger $ diffTimeToPicoseconds $ utctDayTime time
125+ pure (day, dayTime)
126+#else
127+ withCString f $ \f' ->
128+ alloca $ \secPtr ->
129+ alloca $ \nsecPtr -> do
130+ Posix.throwErrnoPathIfMinus1Retry_ "getmodtime" f $ c_getModTime f' secPtr nsecPtr
131+ sec <- peek secPtr
132+ nsec <- peek nsecPtr
133+ pure (fromEnum sec, fromIntegral nsec)
134+
135+-- Sadly even unix’s getFileStatus + modificationTimeHiRes is still about twice as slow
136+-- as doing the FFI call ourselves :(.
137+foreign import ccall "getmodtime" c_getModTime :: CString -> Ptr CTime -> Ptr CLong -> IO Int
138+#endif
123
139
124getFileContentsRule :: VFSHandle -> Rules ()
140getFileContentsRule :: VFSHandle -> Rules ()
125getFileContentsRule vfs =
141getFileContentsRule vfs =
@@ -128,7 +144,7 @@ getFileContentsRule vfs =
128 time <- use_ GetModificationTime file
144 time <- use_ GetModificationTime file
129 res <- liftIO $ ideTryIOException file $ do
145 res <- liftIO $ ideTryIOException file $ do
130 mbVirtual <- getVirtualFile vfs $ filePathToUri' file
146 mbVirtual <- getVirtualFile vfs $ filePathToUri' file
131− pure $ textToStringBuffer . Rope.toText . _text <$> mbVirtual
147+ pure $ Rope.toText . _text <$> mbVirtual
132 case res of
148 case res of
133 Left err -> return ([err], Nothing)
149 Left err -> return ([err], Nothing)
134 Right contents -> return ([], Just (time, contents))
150 Right contents -> return ([], Just (time, contents))
@@ -140,23 +156,14 @@ ideTryIOException fp act =
140 <$> try act
156 <$> try act
141
157
142
158
143−getFileContents :: NormalizedFilePath -> Action (FileVersion, Maybe StringBuffer)
159+getFileContents :: NormalizedFilePath -> Action (FileVersion, Maybe T.Text)
144getFileContents = use_ GetFileContents
160getFileContents = use_ GetFileContents
145
161
146−getFileExists :: NormalizedFilePath -> Action Bool
147−getFileExists =
148− -- we deliberately and intentionally wrap the file as an FilePath WITHOUT mkAbsolute
149− -- so that if the file doesn't exist, is on a shared drive that is unmounted etc we get a properly
150− -- cached 'No' rather than an exception in the wrong place
151− use_ GetFileExists
152−
153−
154fileStoreRules :: VFSHandle -> Rules ()
162fileStoreRules :: VFSHandle -> Rules ()
155fileStoreRules vfs = do
163fileStoreRules vfs = do
156 addIdeGlobal vfs
164 addIdeGlobal vfs
157 getModificationTimeRule vfs
165 getModificationTimeRule vfs
158 getFileContentsRule vfs
166 getFileContentsRule vfs
159− getFileExistsRule vfs
160
167
161
168
162-- | Notify the compiler service that a particular file has been modified.
169-- | Notify the compiler service that a particular file has been modified.
@@ -167,7 +174,7 @@ setBufferModified state absFile contents = do
167 VFSHandle{..} <- getIdeGlobalState state
174 VFSHandle{..} <- getIdeGlobalState state
168 whenJust setVirtualFileContents $ \set ->
175 whenJust setVirtualFileContents $ \set ->
169 set (filePathToUri' absFile) contents
176 set (filePathToUri' absFile) contents
170− void $ shakeRun state [] (const $ pure ())
177+ void $ shakeRun state []
171
178
172-- | Note that some buffer somewhere has been modified, but don't say what.
179-- | Note that some buffer somewhere has been modified, but don't say what.
173-- Only valid if the virtual file system was initialised by LSP, as that
180-- Only valid if the virtual file system was initialised by LSP, as that
@@ -177,4 +184,4 @@ setSomethingModified state = do
177 VFSHandle{..} <- getIdeGlobalState state
184 VFSHandle{..} <- getIdeGlobalState state
178 when (isJust setVirtualFileContents) $
185 when (isJust setVirtualFileContents) $
179 fail "setSomethingModified can't be called on this type of VFSHandle"
186 fail "setSomethingModified can't be called on this type of VFSHandle"
180− void $ shakeRun state [] (const $ pure ())
187+ void $ shakeRun state []
src/Development/IDE/Core/IdeConfiguration.hsnew file+71
@@ -0,0 +1,71 @@
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+30−29
@@ -1,81 +1,82 @@
1−-- Copyright (c) 2019 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
1+-- Copyright (c) 2019 The DAML Authors. All rights reserved.
2-- SPDX-License-Identifier: Apache-2.0
2-- SPDX-License-Identifier: Apache-2.0
3
3
4−
5−{-# LANGUAGE TypeFamilies #-}
4+{-# LANGUAGE TypeFamilies #-}
6−{-# LANGUAGE OverloadedStrings #-}
7{-# LANGUAGE FlexibleInstances #-}
5{-# LANGUAGE FlexibleInstances #-}
8
6
9−-- | A Shake implementation of the compiler service, built
7+-- | Utilities and state for the files of interest - those which are currently
10−-- using the "Shaker" abstraction layer for in-memory use.
8+-- open in the editor. The useful function is 'getFilesOfInterest'.
11−--
12module Development.IDE.Core.OfInterest(
9module Development.IDE.Core.OfInterest(
13 ofInterestRules,
10 ofInterestRules,
14 getFilesOfInterest, setFilesOfInterest, modifyFilesOfInterest,
11 getFilesOfInterest, setFilesOfInterest, modifyFilesOfInterest,
15 ) where
12 ) where
16
13
17−import Control.Concurrent.Extra
14+import Control.Concurrent.Extra
18−import Control.Monad.Except
15+import Data.Binary
19import Data.Hashable
16import Data.Hashable
20import Control.DeepSeq
17import Control.DeepSeq
21import GHC.Generics
18import GHC.Generics
22import Data.Typeable
19import Data.Typeable
23import qualified Data.ByteString.UTF8 as BS
20import qualified Data.ByteString.UTF8 as BS
24import Control.Exception
21import Control.Exception
25−import Development.IDE.Types.Location
22+import Data.HashSet (HashSet)
26−import Development.IDE.Types.Logger
27−import Data.Set (Set)
28−import qualified Data.Set as Set
23+import qualified Data.HashSet as HashSet
29import qualified Data.Text as T
24import qualified Data.Text as T
30import Data.Tuple.Extra
25import Data.Tuple.Extra
31−import Development.Shake hiding (Diagnostic, Env, newCache)
26+import Data.Functor
32−
33−import Development.IDE.Core.Shake
27+import Development.Shake
34
28
29+import Development.IDE.Types.Location
30+import Development.IDE.Types.Logger
31+import Development.IDE.Core.Shake
35
32
36
33
37−newtype OfInterestVar = OfInterestVar (Var (Set NormalizedFilePath))
34+newtype OfInterestVar = OfInterestVar (Var (HashSet NormalizedFilePath))
38instance IsIdeGlobal OfInterestVar
35instance IsIdeGlobal OfInterestVar
39
36
40−
41−type instance RuleResult GetFilesOfInterest = Set NormalizedFilePath
37+type instance RuleResult GetFilesOfInterest = HashSet NormalizedFilePath
42−
43
38
44data GetFilesOfInterest = GetFilesOfInterest
39data GetFilesOfInterest = GetFilesOfInterest
45 deriving (Eq, Show, Typeable, Generic)
40 deriving (Eq, Show, Typeable, Generic)
46instance Hashable GetFilesOfInterest
41instance Hashable GetFilesOfInterest
47instance NFData GetFilesOfInterest
42instance NFData GetFilesOfInterest
43+instance Binary GetFilesOfInterest
48
44
49
45
46+-- | The rule that initialises the files of interest state.
50ofInterestRules :: Rules ()
47ofInterestRules :: Rules ()
51ofInterestRules = do
48ofInterestRules = do
52− addIdeGlobal . OfInterestVar =<< liftIO (newVar Set.empty)
49+ addIdeGlobal . OfInterestVar =<< liftIO (newVar HashSet.empty)
53 defineEarlyCutoff $ \GetFilesOfInterest _file -> assert (null $ fromNormalizedFilePath _file) $ do
50 defineEarlyCutoff $ \GetFilesOfInterest _file -> assert (null $ fromNormalizedFilePath _file) $ do
54 alwaysRerun
51 alwaysRerun
55 filesOfInterest <- getFilesOfInterestUntracked
52 filesOfInterest <- getFilesOfInterestUntracked
56 pure (Just $ BS.fromString $ show filesOfInterest, ([], Just filesOfInterest))
53 pure (Just $ BS.fromString $ show filesOfInterest, ([], Just filesOfInterest))
57
54
58
55
56+-- | Get the files that are open in the IDE.
59−getFilesOfInterest :: Action (Set NormalizedFilePath)
57+getFilesOfInterest :: Action (HashSet NormalizedFilePath)
60−getFilesOfInterest = use_ GetFilesOfInterest ""
58+getFilesOfInterest = useNoFile_ GetFilesOfInterest
61
59
62
60
63
61
64------------------------------------------------------------
62------------------------------------------------------------
65-- Exposed API
63-- Exposed API
66
64
67−-- | Set the files-of-interest which will be built and kept-up-to-date.
65+-- | Set the files-of-interest - not usually necessary or advisable.
66+-- The LSP client will keep this information up to date.
68−setFilesOfInterest :: IdeState -> Set NormalizedFilePath -> IO ()
67+setFilesOfInterest :: IdeState -> HashSet NormalizedFilePath -> IO ()
69setFilesOfInterest state files = modifyFilesOfInterest state (const files)
68setFilesOfInterest state files = modifyFilesOfInterest state (const files)
70
69
71−getFilesOfInterestUntracked :: Action (Set NormalizedFilePath)
70+getFilesOfInterestUntracked :: Action (HashSet NormalizedFilePath)
72getFilesOfInterestUntracked = do
71getFilesOfInterestUntracked = do
73 OfInterestVar var <- getIdeGlobalAction
72 OfInterestVar var <- getIdeGlobalAction
74 liftIO $ readVar var
73 liftIO $ readVar var
75
74
75+-- | Modify the files-of-interest - not usually necessary or advisable.
76+-- The LSP client will keep this information up to date.
76−modifyFilesOfInterest :: IdeState -> (Set NormalizedFilePath -> Set NormalizedFilePath) -> IO ()
77+modifyFilesOfInterest :: IdeState -> (HashSet NormalizedFilePath -> HashSet NormalizedFilePath) -> IO ()
77modifyFilesOfInterest state f = do
78modifyFilesOfInterest state f = do
78 OfInterestVar var <- getIdeGlobalState state
79 OfInterestVar var <- getIdeGlobalState state
79 files <- modifyVar var $ pure . dupe . f
80 files <- modifyVar var $ pure . dupe . f
80− logDebug (ideLogger state) $ "Set files of interest to: " <> T.pack (show $ Set.toList files)
81+ logDebug (ideLogger state) $ "Set files of interest to: " <> T.pack (show $ HashSet.toList files)
81− void $ shakeRun state [] (const $ pure ())
82+ void $ shakeRun state []
src/Development/IDE/Core/PositionMapping.hsnew file+122
@@ -0,0 +1,122 @@
1+-- Copyright (c) 2019 The DAML Authors. All rights reserved.
2+-- SPDX-License-Identifier: Apache-2.0
3+module Development.IDE.Core.PositionMapping
4+ ( PositionMapping(..)
5+ , fromCurrentPosition
6+ , toCurrentPosition
7+ , PositionDelta(..)
8+ , addDelta
9+ , mkDelta
10+ , toCurrentRange
11+ , fromCurrentRange
12+ , applyChange
13+ , zeroMapping
14+ -- toCurrent and fromCurrent are mainly exposed for testing
15+ , toCurrent
16+ , fromCurrent
17+ ) where
18+
19+import Control.Monad
20+import qualified Data.Text as T
21+import Language.Haskell.LSP.Types
22+import Data.List
23+
24+-- The position delta is the difference between two versions
25+data PositionDelta = PositionDelta
26+ { toDelta :: !(Position -> Maybe Position)
27+ , fromDelta :: !(Position -> Maybe Position)
28+ }
29+
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+
41+toCurrentRange :: PositionMapping -> Range -> Maybe Range
42+toCurrentRange mapping (Range a b) =
43+ Range <$> toCurrentPosition mapping a <*> toCurrentPosition mapping b
44+
45+fromCurrentRange :: PositionMapping -> Range -> Maybe Range
46+fromCurrentRange mapping (Range a b) =
47+ Range <$> fromCurrentPosition mapping a <*> fromCurrentPosition mapping b
48+
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)
71+
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
76+ }
77+applyChange posMapping _ = posMapping
78+
79+toCurrent :: Range -> T.Text -> Position -> Maybe Position
80+toCurrent (Range (Position startLine startColumn) (Position endLine endColumn)) t (Position line column)
81+ | line < startLine || line == startLine && column < startColumn =
82+ -- Position is before the change and thereby unchanged.
83+ Just $ Position line column
84+ | line > endLine || line == endLine && column >= endColumn =
85+ -- Position is after the change so increase line and column number
86+ -- as necessary.
87+ Just $ Position (line + lineDiff) newColumn
88+ | otherwise = Nothing
89+ -- Position is in the region that was changed.
90+ where
91+ lineDiff = linesNew - linesOld
92+ linesNew = T.count "\n" t
93+ linesOld = endLine - startLine
94+ newEndColumn
95+ | linesNew == 0 = startColumn + T.length t
96+ | otherwise = T.length $ T.takeWhileEnd (/= '\n') t
97+ newColumn
98+ | line == endLine = column + newEndColumn - endColumn
99+ | otherwise = column
100+
101+fromCurrent :: Range -> T.Text -> Position -> Maybe Position
102+fromCurrent (Range (Position startLine startColumn) (Position endLine endColumn)) t (Position line column)
103+ | line < startLine || line == startLine && column < startColumn =
104+ -- Position is before the change and thereby unchanged
105+ Just $ Position line column
106+ | line > newEndLine || line == newEndLine && column >= newEndColumn =
107+ -- Position is after the change so increase line and column number
108+ -- as necessary.
109+ Just $ Position (line - lineDiff) newColumn
110+ | otherwise = Nothing
111+ -- Position is in the region that was changed.
112+ where
113+ lineDiff = linesNew - linesOld
114+ linesNew = T.count "\n" t
115+ linesOld = endLine - startLine
116+ newEndLine = endLine + lineDiff
117+ newEndColumn
118+ | linesNew == 0 = startColumn + T.length t
119+ | otherwise = T.length $ T.takeWhileEnd (/= '\n') t
120+ newColumn
121+ | line == newEndLine = column - (newEndColumn - endColumn)
122+ | otherwise = column
src/Development/IDE/Core/Preprocessor.hsnew file+230
@@ -0,0 +1,230 @@
1+-- Copyright (c) 2019 The DAML Authors. All rights reserved.
2+-- SPDX-License-Identifier: Apache-2.0
3+
4+module Development.IDE.Core.Preprocessor
5+ ( preprocessor
6+ ) where
7+
8+import Development.IDE.GHC.CPP
9+import Development.IDE.GHC.Orphans()
10+import Development.IDE.GHC.Compat
11+import GhcMonad
12+import StringBuffer as SB
13+
14+import Data.List.Extra
15+import System.FilePath
16+import System.IO.Extra
17+import Data.Char
18+import DynFlags
19+import qualified HeaderInfo as Hdr
20+import Development.IDE.Types.Diagnostics
21+import Development.IDE.Types.Location
22+import Development.IDE.GHC.Error
23+import SysTools (Option (..), runUnlit, runPp)
24+import Control.Monad.Trans.Except
25+import qualified GHC.LanguageExtensions as LangExt
26+import Data.Maybe
27+import Control.Exception.Safe (catch, throw)
28+import Data.IORef (IORef, modifyIORef, newIORef, readIORef)
29+import Data.Text (Text)
30+import qualified Data.Text as T
31+import Outputable (showSDoc)
32+import Control.DeepSeq (NFData(rnf))
33+import Control.Exception (evaluate)
34+import Control.Monad.IO.Class (MonadIO)
35+import Exception (ExceptionMonad)
36+
37+
38+-- | Given a file and some contents, apply any necessary preprocessors,
39+-- 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)
41+preprocessor filename mbContents = do
42+ -- Perform unlit
43+ (isOnDisk, contents) <-
44+ if isLiterate filename then do
45+ dflags <- getDynFlags
46+ newcontent <- liftIO $ runLhs dflags filename mbContents
47+ return (False, newcontent)
48+ else do
49+ contents <- liftIO $ maybe (hGetStringBuffer filename) return mbContents
50+ let isOnDisk = isNothing mbContents
51+ return (isOnDisk, contents)
52+
53+ -- Perform cpp
54+ dflags <- ExceptT $ parsePragmasIntoDynFlags filename contents
55+ (isOnDisk, contents, dflags) <-
56+ if not $ xopt LangExt.Cpp dflags then
57+ return (isOnDisk, contents, dflags)
58+ else do
59+ cppLogs <- liftIO $ newIORef []
60+ contents <- ExceptT
61+ $ liftIO
62+ $ (Right <$> (runCpp dflags {log_action = logAction cppLogs} filename
63+ $ if isOnDisk then Nothing else Just contents))
64+ `catch`
65+ ( \(e :: GhcException) -> do
66+ logs <- readIORef cppLogs
67+ case diagsFromCPPLogs filename (reverse logs) of
68+ [] -> throw e
69+ diags -> return $ Left diags
70+ )
71+ dflags <- ExceptT $ parsePragmasIntoDynFlags filename contents
72+ return (False, contents, dflags)
73+
74+ -- Perform preprocessor
75+ if not $ gopt Opt_Pp dflags then
76+ return (contents, dflags)
77+ else do
78+ contents <- liftIO $ runPreprocessor dflags filename $ if isOnDisk then Nothing else Just contents
79+ dflags <- ExceptT $ parsePragmasIntoDynFlags filename contents
80+ return (contents, dflags)
81+ where
82+ logAction :: IORef [CPPLog] -> LogAction
83+ logAction cppLogs dflags _reason severity srcSpan _style msg = do
84+ let log = CPPLog severity srcSpan $ T.pack $ showSDoc dflags msg
85+ modifyIORef cppLogs (log :)
86+
87+
88+data CPPLog = CPPLog Severity SrcSpan Text
89+ deriving (Show)
90+
91+
92+data CPPDiag
93+ = CPPDiag
94+ { cdRange :: Range,
95+ cdSeverity :: Maybe DiagnosticSeverity,
96+ cdMessage :: [Text]
97+ }
98+ deriving (Show)
99+
100+
101+diagsFromCPPLogs :: FilePath -> [CPPLog] -> [FileDiagnostic]
102+diagsFromCPPLogs filename logs =
103+ map (\d -> (toNormalizedFilePath' filename, ShowDiag, cppDiagToDiagnostic d)) $
104+ go [] logs
105+ where
106+ -- On errors, CPP calls logAction with a real span for the initial log and
107+ -- then additional informational logs with `UnhelpfulSpan`. Collect those
108+ -- informational log messages and attaches them to the initial log message.
109+ go :: [CPPDiag] -> [CPPLog] -> [CPPDiag]
110+ go acc [] = reverse $ map (\d -> d {cdMessage = reverse $ cdMessage d}) acc
111+ go acc (CPPLog sev span@(RealSrcSpan _) msg : logs) =
112+ let diag = CPPDiag (srcSpanToRange span) (toDSeverity sev) [msg]
113+ in go (diag : acc) logs
114+ go (diag : diags) (CPPLog _sev (UnhelpfulSpan _) msg : logs) =
115+ go (diag {cdMessage = msg : cdMessage diag} : diags) logs
116+ go [] (CPPLog _sev (UnhelpfulSpan _) _msg : logs) = go [] logs
117+ cppDiagToDiagnostic :: CPPDiag -> Diagnostic
118+ cppDiagToDiagnostic d =
119+ Diagnostic
120+ { _range = cdRange d,
121+ _severity = cdSeverity d,
122+ _code = Nothing,
123+ _source = Just "CPP",
124+ _message = T.unlines $ cdMessage d,
125+ _relatedInformation = Nothing,
126+ _tags = Nothing
127+ }
128+
129+
130+isLiterate :: FilePath -> Bool
131+isLiterate x = takeExtension x `elem` [".lhs",".lhs-boot"]
132+
133+
134+-- | This reads the pragma information directly from the provided buffer.
135+parsePragmasIntoDynFlags
136+ :: (ExceptionMonad m, HasDynFlags m, MonadIO m)
137+ => FilePath
138+ -> SB.StringBuffer
139+ -> m (Either [FileDiagnostic] DynFlags)
140+parsePragmasIntoDynFlags fp contents = catchSrcErrors "pragmas" $ do
141+ dflags0 <- getDynFlags
142+ 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+
147+ (dflags, _, _) <- parseDynamicFilePragma dflags0 opts
148+ return dflags
149+
150+
151+-- | Run (unlit) literate haskell preprocessor on a file, or buffer if set
152+runLhs :: DynFlags -> FilePath -> Maybe SB.StringBuffer -> IO SB.StringBuffer
153+runLhs dflags filename contents = withTempDir $ \dir -> do
154+ let fout = dir </> takeFileName filename <.> "unlit"
155+ filesrc <- case contents of
156+ Nothing -> return filename
157+ Just cnts -> do
158+ let fsrc = dir </> takeFileName filename <.> "literate"
159+ withBinaryFile fsrc WriteMode $ \h ->
160+ hPutStringBuffer h cnts
161+ return fsrc
162+ unlit filesrc fout
163+ SB.hGetStringBuffer fout
164+ where
165+ unlit filein fileout = SysTools.runUnlit dflags (args filein fileout)
166+ args filein fileout = [
167+ SysTools.Option "-h"
168+ , SysTools.Option (escape filename) -- name this file
169+ , SysTools.FileOption "" filein -- input file
170+ , SysTools.FileOption "" fileout ] -- output file
171+ -- taken from ghc's DriverPipeline.hs
172+ escape ('\\':cs) = '\\':'\\': escape cs
173+ escape ('\"':cs) = '\\':'\"': escape cs
174+ escape ('\'':cs) = '\\':'\'': escape cs
175+ escape (c:cs) = c : escape cs
176+ escape [] = []
177+
178+-- | Run CPP on a file
179+runCpp :: DynFlags -> FilePath -> Maybe SB.StringBuffer -> IO SB.StringBuffer
180+runCpp dflags filename contents = withTempDir $ \dir -> do
181+ let out = dir </> takeFileName filename <.> "out"
182+ dflags <- pure $ addOptP "-D__GHCIDE__" dflags
183+
184+ case contents of
185+ Nothing -> do
186+ -- Happy case, file is not modified, so run CPP on it in-place
187+ -- which also makes things like relative #include files work
188+ -- and means location information is correct
189+ doCpp dflags True filename out
190+ liftIO $ SB.hGetStringBuffer out
191+
192+ Just contents -> do
193+ -- Sad path, we have to create a version of the path in a temp dir
194+ -- __FILE__ macro is wrong, ignoring that for now (likely not a real issue)
195+
196+ -- Relative includes aren't going to work, so we fix that by adding to the include path.
197+ dflags <- return $ addIncludePathsQuote (takeDirectory filename) dflags
198+
199+ -- Location information is wrong, so we fix that by patching it afterwards.
200+ let inp = dir </> "___GHCIDE_MAGIC___"
201+ withBinaryFile inp WriteMode $ \h ->
202+ hPutStringBuffer h contents
203+ doCpp dflags True inp out
204+
205+ -- Fix up the filename in lines like:
206+ -- # 1 "C:/Temp/extra-dir-914611385186/___GHCIDE_MAGIC___"
207+ let tweak x
208+ | Just x <- stripPrefix "# " x
209+ , "___GHCIDE_MAGIC___" `isInfixOf` x
210+ , let num = takeWhile (not . isSpace) x
211+ -- important to use /, and never \ for paths, even on Windows, since then C escapes them
212+ -- and GHC gets all confused
213+ = "# " <> num <> " \"" <> map (\x -> if isPathSeparator x then '/' else x) filename <> "\""
214+ | otherwise = x
215+ stringToStringBuffer . unlines . map tweak . lines <$> readFileUTF8' out
216+
217+
218+-- | Run a preprocessor on a file
219+runPreprocessor :: DynFlags -> FilePath -> Maybe SB.StringBuffer -> IO SB.StringBuffer
220+runPreprocessor dflags filename contents = withTempDir $ \dir -> do
221+ let out = dir </> takeFileName filename <.> "out"
222+ inp <- case contents of
223+ Nothing -> return filename
224+ Just contents -> do
225+ let inp = dir </> takeFileName filename <.> "hs"
226+ withBinaryFile inp WriteMode $ \h ->
227+ hPutStringBuffer h contents
228+ return inp
229+ runPp dflags [SysTools.Option filename, SysTools.Option inp, SysTools.FileOption "" out]
230+ SB.hGetStringBuffer out
src/Development/IDE/Core/RuleTypes.hs+92−18
@@ -1,4 +1,4 @@
1−-- Copyright (c) 2019 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
1+-- Copyright (c) 2019 The DAML Authors. All rights reserved.
2-- SPDX-License-Identifier: Apache-2.0
2-- SPDX-License-Identifier: Apache-2.0
3
3
4{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE FlexibleInstances #-}
@@ -12,18 +12,21 @@ module Development.IDE.Core.RuleTypes(
12 ) where
12 ) where
13
13
14import Control.DeepSeq
14import Control.DeepSeq
15−import Development.IDE.Core.Compile (TcModuleResult)
15+import Data.Binary
16−import Development.IDE.Import.FindImports (Import(..))
17import Development.IDE.Import.DependencyInformation
16import Development.IDE.Import.DependencyInformation
17+import Development.IDE.GHC.Util
18import Data.Hashable
18import Data.Hashable
19import Data.Typeable
19import Data.Typeable
20+import qualified Data.Set as S
20−import Development.Shake hiding (Env, newCache)
21+import Development.Shake
21import GHC.Generics (Generic)
22import GHC.Generics (Generic)
22
23
23import GHC
24import GHC
24−import Development.IDE.GHC.Compat
25+import Module (InstalledUnitId)
26+import HscTypes (CgGuts, Linkable, HomeModInfo, ModDetails)
25
27
26import Development.IDE.Spans.Type
28import Development.IDE.Spans.Type
29+import Development.IDE.Import.FindImports (ArtifactsLocation)
27
30
28
31
29-- NOTATION
32-- NOTATION
@@ -42,79 +45,150 @@ type instance RuleResult GetDependencyInformation = DependencyInformation
42-- This rule is also responsible for calling ReportImportCycles for each file in the transitive closure.
45-- This rule is also responsible for calling ReportImportCycles for each file in the transitive closure.
43type instance RuleResult GetDependencies = TransitiveDependencies
46type instance RuleResult GetDependencies = TransitiveDependencies
44
47
48+-- | Contains the typechecked module and the OrigNameCache entry for
49+-- that module.
50+data TcModuleResult = TcModuleResult
51+ { tmrModule :: TypecheckedModule
52+ , tmrModInfo :: HomeModInfo
53+ }
54+instance Show TcModuleResult where
55+ show = show . pm_mod_summary . tm_parsed_module . tmrModule
56+
57+instance NFData TcModuleResult where
58+ rnf = rwhnf
59+
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+
45-- | The type checked version of this file, requires TypeCheck+
76-- | The type checked version of this file, requires TypeCheck+
46type instance RuleResult TypeCheck = TcModuleResult
77type instance RuleResult TypeCheck = TcModuleResult
47
78
48-- | Information about what spans occur where, requires TypeCheck
79-- | Information about what spans occur where, requires TypeCheck
49−type instance RuleResult GetSpanInfo = [SpanInfo]
80+type instance RuleResult GetSpanInfo = SpansInfo
50
81
51-- | Convert to Core, requires TypeCheck*
82-- | Convert to Core, requires TypeCheck*
52−type instance RuleResult GenerateCore = CoreModule
83+type instance RuleResult GenerateCore = (SafeHaskellMode, CgGuts, ModDetails)
84+
85+-- | Generate byte code for template haskell.
86+type instance RuleResult GenerateByteCode = Linkable
53
87
54-- | A GHC session that we reuse.
88-- | A GHC session that we reuse.
55−type instance RuleResult GhcSession = HscEnv
89+type instance RuleResult GhcSession = HscEnvEq
56
90
57−-- | Resolve the imports in a module to the list of either external packages or absolute file paths
91+-- | Resolve the imports in a module to the file path of a module
58−-- for modules in the same package.
92+-- in the same package or the package id of another package.
59−type instance RuleResult GetLocatedImports = [(Located ModuleName, Maybe Import)]
93+type instance RuleResult GetLocatedImports = ([(Located ModuleName, Maybe ArtifactsLocation)], S.Set InstalledUnitId)
60
94
61-- | This rule is used to report import cycles. It depends on GetDependencyInformation.
95-- | This rule is used to report import cycles. It depends on GetDependencyInformation.
62-- We cannot report the cycles directly from GetDependencyInformation since
96-- We cannot report the cycles directly from GetDependencyInformation since
63-- we can only report diagnostics for the current file.
97-- we can only report diagnostics for the current file.
64type instance RuleResult ReportImportCycles = ()
98type instance RuleResult ReportImportCycles = ()
65
99
66−-- | Read the given HIE file.
100+-- | Read the module interface file
67−type instance RuleResult GetHieFile = HieFile
101+type instance RuleResult GetHiFile = HiFileResult
102+
103+-- | Get a module interface, either from an interface file or a typechecked module
104+type instance RuleResult GetModIface = HiFileResult
68
105
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
69
111
70data GetParsedModule = GetParsedModule
112data GetParsedModule = GetParsedModule
71 deriving (Eq, Show, Typeable, Generic)
113 deriving (Eq, Show, Typeable, Generic)
72instance Hashable GetParsedModule
114instance Hashable GetParsedModule
73instance NFData GetParsedModule
115instance NFData GetParsedModule
116+instance Binary GetParsedModule
74
117
75data GetLocatedImports = GetLocatedImports
118data GetLocatedImports = GetLocatedImports
76 deriving (Eq, Show, Typeable, Generic)
119 deriving (Eq, Show, Typeable, Generic)
77instance Hashable GetLocatedImports
120instance Hashable GetLocatedImports
78instance NFData GetLocatedImports
121instance NFData GetLocatedImports
122+instance Binary GetLocatedImports
79
123
80data GetDependencyInformation = GetDependencyInformation
124data GetDependencyInformation = GetDependencyInformation
81 deriving (Eq, Show, Typeable, Generic)
125 deriving (Eq, Show, Typeable, Generic)
82instance Hashable GetDependencyInformation
126instance Hashable GetDependencyInformation
83instance NFData GetDependencyInformation
127instance NFData GetDependencyInformation
128+instance Binary GetDependencyInformation
84
129
85data ReportImportCycles = ReportImportCycles
130data ReportImportCycles = ReportImportCycles
86 deriving (Eq, Show, Typeable, Generic)
131 deriving (Eq, Show, Typeable, Generic)
87instance Hashable ReportImportCycles
132instance Hashable ReportImportCycles
88instance NFData ReportImportCycles
133instance NFData ReportImportCycles
134+instance Binary ReportImportCycles
89
135
90data GetDependencies = GetDependencies
136data GetDependencies = GetDependencies
91 deriving (Eq, Show, Typeable, Generic)
137 deriving (Eq, Show, Typeable, Generic)
92instance Hashable GetDependencies
138instance Hashable GetDependencies
93instance NFData GetDependencies
139instance NFData GetDependencies
140+instance Binary GetDependencies
94
141
95data TypeCheck = TypeCheck
142data TypeCheck = TypeCheck
96 deriving (Eq, Show, Typeable, Generic)
143 deriving (Eq, Show, Typeable, Generic)
97instance Hashable TypeCheck
144instance Hashable TypeCheck
98instance NFData TypeCheck
145instance NFData TypeCheck
146+instance Binary TypeCheck
99
147
100data GetSpanInfo = GetSpanInfo
148data GetSpanInfo = GetSpanInfo
101 deriving (Eq, Show, Typeable, Generic)
149 deriving (Eq, Show, Typeable, Generic)
102instance Hashable GetSpanInfo
150instance Hashable GetSpanInfo
103instance NFData GetSpanInfo
151instance NFData GetSpanInfo
152+instance Binary GetSpanInfo
104
153
105data GenerateCore = GenerateCore
154data GenerateCore = GenerateCore
106 deriving (Eq, Show, Typeable, Generic)
155 deriving (Eq, Show, Typeable, Generic)
107instance Hashable GenerateCore
156instance Hashable GenerateCore
108instance NFData GenerateCore
157instance NFData GenerateCore
158+instance Binary GenerateCore
159+
160+data GenerateByteCode = GenerateByteCode
161+ deriving (Eq, Show, Typeable, Generic)
162+instance Hashable GenerateByteCode
163+instance NFData GenerateByteCode
164+instance Binary GenerateByteCode
109
165
110data GhcSession = GhcSession
166data GhcSession = GhcSession
111 deriving (Eq, Show, Typeable, Generic)
167 deriving (Eq, Show, Typeable, Generic)
112instance Hashable GhcSession
168instance Hashable GhcSession
113instance NFData GhcSession
169instance NFData GhcSession
170+instance Binary GhcSession
171+
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
185+ deriving (Eq, Show, Typeable, Generic)
186+instance Hashable IsFileOfInterest
187+instance NFData IsFileOfInterest
188+instance Binary IsFileOfInterest
114
189
115−-- Note that we embed the filepath here instead of using the filepath associated with Shake keys.
190+data GetModSummary = GetModSummary
116−-- Otherwise we will garbage collect the result since files in package dependencies will not be declared reachable.
117−data GetHieFile = GetHieFile FilePath
118 deriving (Eq, Show, Typeable, Generic)
191 deriving (Eq, Show, Typeable, Generic)
119−instance Hashable GetHieFile
192+instance Hashable GetModSummary
120−instance NFData GetHieFile
193+instance NFData GetModSummary
194+instance Binary GetModSummary
src/Development/IDE/Core/Rules.hs+520−147
@@ -1,56 +1,81 @@
1−-- Copyright (c) 2019 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
1+-- Copyright (c) 2019 The DAML Authors. All rights reserved.
2-- SPDX-License-Identifier: Apache-2.0
2-- SPDX-License-Identifier: Apache-2.0
3
3
4+{-# LANGUAGE CPP #-}
4−{-# LANGUAGE TypeFamilies #-}
5+{-# LANGUAGE TypeFamilies #-}
5−{-# LANGUAGE OverloadedStrings #-}
6−{-# LANGUAGE FlexibleInstances #-}
6+{-# LANGUAGE FlexibleInstances #-}
7{-# LANGUAGE DuplicateRecordFields #-}
7{-# LANGUAGE DuplicateRecordFields #-}
8+#include "ghc-api-version.h"
8
9
9-- | A Shake implementation of the compiler service, built
10-- | A Shake implementation of the compiler service, built
10-- using the "Shaker" abstraction layer for in-memory use.
11-- using the "Shaker" abstraction layer for in-memory use.
11--
12--
12module Development.IDE.Core.Rules(
13module Development.IDE.Core.Rules(
13 IdeState, GetDependencies(..), GetParsedModule(..), TransitiveDependencies(..),
14 IdeState, GetDependencies(..), GetParsedModule(..), TransitiveDependencies(..),
14− Priority(..),
15+ Priority(..), GhcSessionIO(..), GhcSessionFun(..),
16+ priorityTypeCheck,
17+ priorityGenerateCore,
18+ priorityFilesOfInterest,
15− runAction, runActions, useE, usesE,
19+ runAction, useE, useNoFileE, usesE,
16 toIdeResult, defineNoFile,
20 toIdeResult, defineNoFile,
17 mainRule,
21 mainRule,
18− getGhcCore,
19 getAtPoint,
22 getAtPoint,
20 getDefinition,
23 getDefinition,
21 getDependencies,
24 getDependencies,
22 getParsedModule,
25 getParsedModule,
23− fileFromParsedModule
26+ generateCore,
24 ) where
27 ) where
25
28
29+import Fingerprint
30+
31+import Data.Binary hiding (get, put)
32+import Data.Bifunctor (first, second)
26−import Control.Monad.Except
33+import Control.Monad.Extra
34+import Control.Monad.Trans.Class
27import Control.Monad.Trans.Maybe
35import Control.Monad.Trans.Maybe
28−import qualified Development.IDE.Core.Compile as Compile
36+import Development.IDE.Core.Compile
37+import Development.IDE.Core.OfInterest
29−import qualified Development.IDE.Types.Options as Compile
38+import Development.IDE.Types.Options
39+import Development.IDE.Spans.Calculate
30import Development.IDE.Import.DependencyInformation
40import Development.IDE.Import.DependencyInformation
31import Development.IDE.Import.FindImports
41import Development.IDE.Import.FindImports
32−import Development.IDE.Core.FileStore
42+import Development.IDE.Core.FileExists
43+import Development.IDE.Core.FileStore (getFileContents)
33−import Development.IDE.Types.Diagnostics as Base
44+import Development.IDE.Types.Diagnostics
34import Development.IDE.Types.Location
45import Development.IDE.Types.Location
35−import Data.Bifunctor
46+import Development.IDE.GHC.Compat hiding (parseModule, typecheckModule)
47+import Development.IDE.GHC.Util
48+import Development.IDE.GHC.WithDynFlags
36import Data.Either.Extra
49import Data.Either.Extra
37import Data.Maybe
50import Data.Maybe
38import Data.Foldable
51import Data.Foldable
39−import qualified Data.Map.Strict as Map
52+import qualified Data.IntMap.Strict as IntMap
53+import Data.IntMap.Strict (IntMap)
54+import Data.List
55+import Data.Ord
40import qualified Data.Set as Set
56import qualified Data.Set as Set
41import qualified Data.Text as T
57import qualified Data.Text as T
42import Development.IDE.GHC.Error
58import Development.IDE.GHC.Error
43−import Development.Shake hiding (Diagnostic, Env, newCache)
59+import Development.Shake hiding (Diagnostic)
44import Development.IDE.Core.RuleTypes
60import Development.IDE.Core.RuleTypes
61+import Development.IDE.Spans.Type
62+import qualified Data.ByteString.Char8 as BS
45
63
46−import GHC
64+import qualified GHC.LanguageExtensions as LangExt
47−import Development.IDE.GHC.Compat
65+import HscTypes
48−import UniqSupply
66+import PackageConfig
49−import NameCache
67+import DynFlags (gopt_set, xopt)
68+import GHC.Generics(Generic)
50
69
51import qualified Development.IDE.Spans.AtPoint as AtPoint
70import qualified Development.IDE.Spans.AtPoint as AtPoint
52import Development.IDE.Core.Service
71import Development.IDE.Core.Service
53import Development.IDE.Core.Shake
72import 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
54
79
55-- | This is useful for rules to convert rules that can only produce errors or
80-- | This is useful for rules to convert rules that can only produce errors or
56-- a result into the more general IdeResult type that supports producing
81-- a result into the more general IdeResult type that supports producing
@@ -63,28 +88,21 @@ toIdeResult = either (, Nothing) (([],) . Just)
63useE :: IdeRule k v => k -> NormalizedFilePath -> MaybeT Action v
88useE :: IdeRule k v => k -> NormalizedFilePath -> MaybeT Action v
64useE k = MaybeT . use k
89useE k = MaybeT . use k
65
90
91+useNoFileE :: IdeRule k v => k -> MaybeT Action v
92+useNoFileE k = useE k emptyFilePath
93+
66usesE :: IdeRule k v => k -> [NormalizedFilePath] -> MaybeT Action [v]
94usesE :: IdeRule k v => k -> [NormalizedFilePath] -> MaybeT Action [v]
67usesE k = MaybeT . fmap sequence . uses k
95usesE k = MaybeT . fmap sequence . uses k
68
96
69defineNoFile :: IdeRule k v => (k -> Action v) -> Rules ()
97defineNoFile :: IdeRule k v => (k -> Action v) -> Rules ()
70defineNoFile f = define $ \k file -> do
98defineNoFile f = define $ \k file -> do
71− if file == "" then do res <- f k; return ([], Just res) else
99+ if file == emptyFilePath then do res <- f k; return ([], Just res) else
72 fail $ "Rule " ++ show k ++ " should always be called with the empty string for a file"
100 fail $ "Rule " ++ show k ++ " should always be called with the empty string for a file"
73
101
74
102
75------------------------------------------------------------
103------------------------------------------------------------
76-- Exposed API
104-- Exposed API
77
105
78−
79−-- | Generate the GHC Core for the supplied file and its dependencies.
80−getGhcCore :: NormalizedFilePath -> Action (Maybe [CoreModule])
81−getGhcCore file = runMaybeT $ do
82− files <- transitiveModuleDeps <$> useE GetDependencies file
83− pms <- usesE GetParsedModule $ files ++ [file]
84− usesE GenerateCore $ map fileFromParsedModule pms
85−
86−
87−
88-- | Get all transitive file dependencies of a given module.
106-- | Get all transitive file dependencies of a given module.
89-- Does not include the file itself.
107-- Does not include the file itself.
90getDependencies :: NormalizedFilePath -> Action (Maybe [NormalizedFilePath])
108getDependencies :: NormalizedFilePath -> Action (Maybe [NormalizedFilePath])
@@ -94,181 +112,545 @@ getDependencies file = fmap transitiveModuleDeps <$> use GetDependencies file
94getAtPoint :: NormalizedFilePath -> Position -> Action (Maybe (Maybe Range, [T.Text]))
112getAtPoint :: NormalizedFilePath -> Position -> Action (Maybe (Maybe Range, [T.Text]))
95getAtPoint file pos = fmap join $ runMaybeT $ do
113getAtPoint file pos = fmap join $ runMaybeT $ do
96 opts <- lift getIdeOptions
114 opts <- lift getIdeOptions
97− files <- transitiveModuleDeps <$> useE GetDependencies file
98− tms <- usesE TypeCheck (file : files)
99 spans <- useE GetSpanInfo file
115 spans <- useE GetSpanInfo file
100− return $ AtPoint.atPoint opts (map Compile.tmrModule tms) spans pos
116+ return $ AtPoint.atPoint opts spans pos
101
117
102-- | Goto Definition.
118-- | Goto Definition.
103getDefinition :: NormalizedFilePath -> Position -> Action (Maybe Location)
119getDefinition :: NormalizedFilePath -> Position -> Action (Maybe Location)
104getDefinition file pos = fmap join $ runMaybeT $ do
120getDefinition file pos = fmap join $ runMaybeT $ do
105− spans <- useE GetSpanInfo file
106− pkgState <- useE GhcSession ""
107 opts <- lift getIdeOptions
121 opts <- lift getIdeOptions
108− let getHieFile x = use (GetHieFile x) ""
122+ spans <- useE GetSpanInfo file
109− lift $ AtPoint.gotoDefinition getHieFile opts pkgState spans pos
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
110
175
111-- | Parse the contents of a daml file.
176-- | Parse the contents of a daml file.
112getParsedModule :: NormalizedFilePath -> Action (Maybe ParsedModule)
177getParsedModule :: NormalizedFilePath -> Action (Maybe ParsedModule)
113getParsedModule file = use GetParsedModule file
178getParsedModule file = use GetParsedModule file
114
179
115−
116------------------------------------------------------------
180------------------------------------------------------------
117-- Rules
181-- Rules
118-- These typically go from key to value and are oracles.
182-- These typically go from key to value and are oracles.
119
183
120−-- TODO (MK) This should be independent of DAML or move out of hie-core.
184+priorityTypeCheck :: Priority
121−-- | We build artefacts based on the following high-to-low priority order.
185+priorityTypeCheck = Priority 0
186+
122−data Priority
187+priorityGenerateCore :: Priority
123− = PriorityTypeCheck
188+priorityGenerateCore = Priority (-1)
124− | PriorityGenerateDalf
125− | PriorityFilesOfInterest
126− deriving (Eq, Ord, Show, Enum)
127
189
190+priorityFilesOfInterest :: Priority
191+priorityFilesOfInterest = Priority (-2)
128
192
129getParsedModuleRule :: Rules ()
193getParsedModuleRule :: Rules ()
130−getParsedModuleRule =
194+getParsedModuleRule = defineEarlyCutoff $ \GetParsedModule file -> do
131− define $ \GetParsedModule file -> do
132− (_, contents) <- getFileContents file
133− packageState <- use_ GhcSession ""
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)
134− opt <- getIdeOptions
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
135− liftIO $ Compile.parseModule opt packageState (fromNormalizedFilePath file) contents
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))
136
235
137getLocatedImportsRule :: Rules ()
236getLocatedImportsRule :: Rules ()
138getLocatedImportsRule =
237getLocatedImportsRule =
139 define $ \GetLocatedImports file -> do
238 define $ \GetLocatedImports file -> do
140− pm <- use_ GetParsedModule file
239+ ms <- use_ GetModSummary file
141− let ms = pm_mod_summary pm
142− let imports = ms_textual_imps ms
240+ let imports = [(False, imp) | imp <- ms_textual_imps ms] ++ [(True, imp) | imp <- ms_srcimps ms]
143− packageState <- use_ GhcSession ""
241+ env_eq <- use_ GhcSession file
144− dflags <- liftIO $ Compile.getGhcDynFlags pm packageState
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
145 opt <- getIdeOptions
245 opt <- getIdeOptions
146− xs <- forM imports $ \(mbPkgName, modName) ->
246+ (diags, imports') <- fmap unzip $ forM imports $ \(isSource, (mbPkgName, modName)) -> do
147− (modName, ) <$> locateModule dflags (Compile.optExtensions opt) getFileExists modName mbPkgName
247+ diagOrImp <- locateModule dflags import_dirs (optExtensions opt) getFileExists modName mbPkgName isSource
148− return (concat $ lefts $ map snd xs, Just $ map (second eitherToMaybe) xs)
248+ case diagOrImp of
149−
249+ Left diags -> pure (diags, Left (modName, Nothing))
250+ Right (FileImport path) -> pure ([], Left (modName, Just path))
251+ Right (PackageImport pkgId) -> liftIO $ do
252+ diagsOrPkgDeps <- computePackageDeps env pkgId
253+ case diagsOrPkgDeps of
254+ Left diags -> pure (diags, Right Nothing)
255+ Right pkgIds -> pure ([], Right $ Just $ pkgId : pkgIds)
256+ let (moduleImports, pkgImports) = partitionEithers imports'
257+ case sequence pkgImports of
258+ Nothing -> pure (concat diags, Nothing)
259+ Just pkgImports -> pure (concat diags, Just (moduleImports, Set.fromList $ concat pkgImports))
260+
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+ )
150
269
151-- | Given a target file path, construct the raw dependency results by following
270-- | Given a target file path, construct the raw dependency results by following
152-- imports recursively.
271-- imports recursively.
153−rawDependencyInformation :: NormalizedFilePath -> ExceptT [FileDiagnostic] Action RawDependencyInformation
272+rawDependencyInformation :: [NormalizedFilePath] -> Action RawDependencyInformation
154−rawDependencyInformation f = go (Set.singleton f) Map.empty Map.empty
273+rawDependencyInformation fs = do
155− where go fs !modGraph !pkgs =
274+ (rdi, ss) <- execRawDepM (mapM_ go fs)
156− case Set.minView fs of
275+ let bm = IntMap.foldrWithKey (updateBootMap rdi) IntMap.empty ss
276+ return (rdi { rawBootMap = bm })
277+ where
278+ go :: NormalizedFilePath -- ^ Current module being processed
157− Nothing -> pure (RawDependencyInformation modGraph pkgs)
279+ -> StateT (RawDependencyInformation, IntMap ArtifactsLocation) Action FilePathId
158− Just (f, fs) -> do
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
159− importsOrErr <- lift $ use GetLocatedImports f
292+ importsOrErr <- lift $ use GetLocatedImports f
160− case importsOrErr of
293+ case importsOrErr of
161− Nothing ->
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
162− let modGraph' = Map.insert f (Left ModuleParseError) modGraph
298+ modifyRawDepInfo (insertImport fId (Left ModuleParseError))
163− in go fs modGraph' pkgs
299+ return fId
164− Just imports -> do
300+ Just (modImports, pkgImports) -> do
165− packageState <- lift $ use_ GhcSession ""
301+ -- Get NFPs of the imports which have corresponding files
166− modOrPkgImports <- forM imports $ \imp -> do
302+ -- Imports either come locally from a file or from a package.
167− case imp of
303+ let (no_file, with_file) = splitImports modImports
168− (_modName, Just (PackageImport pkg)) -> do
304+ (mns, ls) = unzip with_file
169− pkgs <- ExceptT $ liftIO $ Compile.computePackageDeps packageState pkg
305+ -- Recursively process all the imports we just learnt about
170− pure $ Right $ pkg:pkgs
306+ -- and get back a list of their FilePathIds
171− (modName, Just (FileImport absFile)) -> pure $ Left (modName, Just absFile)
307+ fids <- mapM (go . artifactFilePath) ls
172− (modName, Nothing) -> pure $ Left (modName, Nothing)
308+ -- Associate together the ModuleName with the FilePathId
173− let (modImports, pkgImports) = partitionEithers modOrPkgImports
309+ let moduleImports' = map (,Nothing) no_file ++ zip mns (map Just fids)
174− let newFiles = Set.fromList (mapMaybe snd modImports) Set.\\ Map.keysSet modGraph
310+ -- Insert into the map the information about this modules
175− modGraph' = Map.insert f (Right modImports) modGraph
311+ -- imports.
176− pkgs' = Map.insert f (Set.fromList $ concat pkgImports) pkgs
312+ modifyRawDepInfo $ insertImport fId (Right $ ModuleImports moduleImports' pkgImports)
177− go (fs `Set.union` newFiles) modGraph' pkgs'
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"
178
359
179getDependencyInformationRule :: Rules ()
360getDependencyInformationRule :: Rules ()
180getDependencyInformationRule =
361getDependencyInformationRule =
181− define $ \GetDependencyInformation file -> fmap toIdeResult $ runExceptT $ do
362+ define $ \GetDependencyInformation file -> do
182− rawDepInfo <- rawDependencyInformation file
363+ rawDepInfo <- rawDependencyInformation [file]
183− pure $ processDependencyInformation rawDepInfo
364+ pure ([], Just $ processDependencyInformation rawDepInfo)
184
365
185reportImportCyclesRule :: Rules ()
366reportImportCyclesRule :: Rules ()
186reportImportCyclesRule =
367reportImportCyclesRule =
187 define $ \ReportImportCycles file -> fmap (\errs -> if null errs then ([], Just ()) else (errs, Nothing)) $ do
368 define $ \ReportImportCycles file -> fmap (\errs -> if null errs then ([], Just ()) else (errs, Nothing)) $ do
188 DependencyInformation{..} <- use_ GetDependencyInformation file
369 DependencyInformation{..} <- use_ GetDependencyInformation file
370+ let fileId = pathToId depPathIdMap file
189− case Map.lookup file depErrorNodes of
371+ case IntMap.lookup (getFilePathId fileId) depErrorNodes of
190 Nothing -> pure []
372 Nothing -> pure []
191 Just errs -> do
373 Just errs -> do
192− let cycles = mapMaybe (cycleErrorInFile file) (toList errs)
374+ let cycles = mapMaybe (cycleErrorInFile fileId) (toList errs)
193 -- Convert cycles of files into cycles of module names
375 -- Convert cycles of files into cycles of module names
194 forM cycles $ \(imp, files) -> do
376 forM cycles $ \(imp, files) -> do
377+ modNames <- forM files $ \fileId -> do
378+ let file = idToPath depPathIdMap fileId
195− modNames <- mapM getModuleName files
379+ getModuleName file
196− pure $ toDiag imp modNames
380+ pure $ toDiag imp $ sort modNames
197 where cycleErrorInFile f (PartOfCycle imp fs)
381 where cycleErrorInFile f (PartOfCycle imp fs)
198 | f `elem` fs = Just (imp, fs)
382 | f `elem` fs = Just (imp, fs)
199 cycleErrorInFile _ _ = Nothing
383 cycleErrorInFile _ _ = Nothing
200− toDiag imp mods = (fp ,) $ Diagnostic
384+ toDiag imp mods = (fp , ShowDiag , ) $ Diagnostic
201 { _range = (_range :: Location -> Range) loc
385 { _range = (_range :: Location -> Range) loc
202 , _severity = Just DsError
386 , _severity = Just DsError
203 , _source = Just "Import cycle detection"
387 , _source = Just "Import cycle detection"
204 , _message = "Cyclic module dependency between " <> showCycle mods
388 , _message = "Cyclic module dependency between " <> showCycle mods
205 , _code = Nothing
389 , _code = Nothing
206 , _relatedInformation = Nothing
390 , _relatedInformation = Nothing
391+ , _tags = Nothing
207 }
392 }
208 where loc = srcSpanToLocation (getLoc imp)
393 where loc = srcSpanToLocation (getLoc imp)
209− fp = toNormalizedFilePath $ srcSpanToFilename (getLoc imp)
394+ fp = toNormalizedFilePath' $ srcSpanToFilename (getLoc imp)
210 getModuleName file = do
395 getModuleName file = do
211− pm <- use_ GetParsedModule file
396+ ms <- use_ GetModSummary file
212− pure (moduleNameString . moduleName . ms_mod $ pm_mod_summary pm)
397+ pure (moduleNameString . moduleName . ms_mod $ ms)
213 showCycle mods = T.intercalate ", " (map T.pack mods)
398 showCycle mods = T.intercalate ", " (map T.pack mods)
214
399
215-- returns all transitive dependencies in topological order.
400-- returns all transitive dependencies in topological order.
216-- NOTE: result does not include the argument file.
401-- NOTE: result does not include the argument file.
217getDependenciesRule :: Rules ()
402getDependenciesRule :: Rules ()
218getDependenciesRule =
403getDependenciesRule =
219− define $ \GetDependencies file -> do
404+ defineEarlyCutoff $ \GetDependencies file -> do
220− depInfo@DependencyInformation{..} <- use_ GetDependencyInformation file
405+ depInfo <- use_ GetDependencyInformation file
221− let allFiles = Map.keys depModuleDeps <> Map.keys depErrorNodes
406+ let allFiles = reachableModules depInfo
222 _ <- uses_ ReportImportCycles allFiles
407 _ <- uses_ ReportImportCycles allFiles
408+ opts <- getIdeOptions
409+ let mbFingerprints = map (fingerprintString . fromNormalizedFilePath) allFiles <$ optShakeFiles opts
223− return ([], transitiveDeps depInfo file)
410+ return (fingerprintToBS . fingerprintFingerprints <$> mbFingerprints, ([], transitiveDeps depInfo file))
224
411
225-- Source SpanInfo is used by AtPoint and Goto Definition.
412-- Source SpanInfo is used by AtPoint and Goto Definition.
226getSpanInfoRule :: Rules ()
413getSpanInfoRule :: Rules ()
227getSpanInfoRule =
414getSpanInfoRule =
228 define $ \GetSpanInfo file -> do
415 define $ \GetSpanInfo file -> do
229− pm <- use_ GetParsedModule file
230 tc <- use_ TypeCheck file
416 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
231− imports <- use_ GetLocatedImports file
430+ (fileImports, _) <- use_ GetLocatedImports file
232− packageState <- use_ GhcSession ""
431+ let imports = second (fmap artifactFilePath) <$> fileImports
233− x <- liftIO $ Compile.getSrcSpanInfos pm packageState (fileImports imports) tc
432+ x <- liftIO $ getSrcSpanInfos packageState imports tc parsedDeps (map hirModIface ifaces)
234 return ([], Just x)
433 return ([], Just x)
235
434
236-- Typechecks a module.
435-- Typechecks a module.
237typeCheckRule :: Rules ()
436typeCheckRule :: Rules ()
238−typeCheckRule =
239− define $ \TypeCheck file -> do
437+typeCheckRule = define $ \TypeCheck file -> do
240− pm <- use_ GetParsedModule file
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
241− deps <- use_ GetDependencies file
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
242− tms <- uses_ TypeCheck (transitiveModuleDeps deps)
467+ fmap Just <$> uses_ GenerateByteCode (transitiveModuleDeps deps)
243− setPriority PriorityTypeCheck
468+ else
244− packageState <- use_ GhcSession ""
469+ pure $ repeat Nothing
470+
471+ setPriority priorityTypeCheck
245− opt <- getIdeOptions
472+ IdeOptions { optDefer = defer } <- getIdeOptions
246− liftIO $ Compile.typecheckModule opt packageState tms pm
473+
247−
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
503+ deps <- use_ GetDependencies file
504+ (tm:tms) <- uses_ TypeCheck (file:transitiveModuleDeps deps)
505+ setPriority priorityGenerateCore
506+ packageState <- hscEnv <$> use_ GhcSession file
507+ liftIO $ compileModule runSimplifier packageState [(tmrModSummary x, tmrModInfo x) | x <- tms] tm
248
508
249generateCoreRule :: Rules ()
509generateCoreRule :: Rules ()
250generateCoreRule =
510generateCoreRule =
251− define $ \GenerateCore file -> do
511+ define $ \GenerateCore -> generateCore (RunSimplifier True)
252− deps <- use_ GetDependencies file
253− (tm:tms) <- uses_ TypeCheck (file:transitiveModuleDeps deps)
254− let pm = tm_parsed_module . Compile.tmrModule $ tm
255− setPriority PriorityGenerateDalf
256− packageState <- use_ GhcSession ""
257− liftIO $ Compile.compileModule pm packageState tms tm
258
512
259−loadGhcSession :: Rules ()
513+generateByteCodeRule :: Rules ()
260−loadGhcSession =
514+generateByteCodeRule =
261− defineNoFile $ \GhcSession -> do
515+ define $ \GenerateByteCode file -> do
262− opts <- getIdeOptions
516+ deps <- use_ GetDependencies file
263− Compile.optGhcSession opts
517+ (tm : tms) <- uses_ TypeCheck (file: transitiveModuleDeps deps)
518+ session <- hscEnv <$> use_ GhcSession file
519+ (_, guts, _) <- use_ GenerateCore file
520+ liftIO $ generateByteCode session [(tmrModSummary x, tmrModInfo x) | x <- tms] tm guts
521+
522+-- A local rule type to get caching. We want to use newCache, but it has
523+-- thread killed exception issues, so we lift it to a full rule.
524+-- https://github.com/digital-asset/daml/pull/2808#issuecomment-529639547
525+type instance RuleResult GhcSessionIO = GhcSessionFun
526+
527+data GhcSessionIO = GhcSessionIO deriving (Eq, Show, Typeable, Generic)
528+instance Hashable GhcSessionIO
529+instance NFData GhcSessionIO
530+instance Binary GhcSessionIO
531+
532+newtype GhcSessionFun = GhcSessionFun (FilePath -> Action (IdeResult HscEnvEq))
533+instance Show GhcSessionFun where show _ = "GhcSessionFun"
534+instance NFData GhcSessionFun where rnf !_ = ()
264
535
265
536
266−getHieFileRule :: Rules ()
537+loadGhcSession :: Rules ()
267−getHieFileRule =
538+loadGhcSession = do
268− defineNoFile $ \(GetHieFile f) -> do
539+ defineNoFile $ \GhcSessionIO -> do
269− u <- liftIO $ mkSplitUniqSupply 'a'
540+ opts <- getIdeOptions
270− let nameCache = initNameCache u []
541+ GhcSessionFun <$> optGhcSession opts
271− liftIO $ fmap (hie_file_result . fst) $ readHieFile nameCache f
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
546+ defineEarlyCutoff $ \GhcSession file -> do
547+ GhcSessionFun fun <- useNoFile_ GhcSessionIO
548+ alwaysRerun
549+ val <- fun $ fromNormalizedFilePath file
550+
551+ -- TODO: What was this doing before?
552+ 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)
648+
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))
272
654
273-- | A rule that wires per-file rules together
655-- | A rule that wires per-file rules together
274mainRule :: Rules ()
656mainRule :: Rules ()
@@ -281,18 +663,9 @@ mainRule = do
281 typeCheckRule
663 typeCheckRule
282 getSpanInfoRule
664 getSpanInfoRule
283 generateCoreRule
665 generateCoreRule
666+ generateByteCodeRule
284 loadGhcSession
667 loadGhcSession
285− getHieFileRule
668+ getHiFileRule
286−
669+ getModIfaceRule
287−------------------------------------------------------------
670+ isFileOfInterestRule
288−
671+ getModSummaryRule
289−fileFromParsedModule :: ParsedModule -> NormalizedFilePath
290−fileFromParsedModule = toNormalizedFilePath . ms_hspp_file . pm_mod_summary
291−
292−fileImports ::
293− [(Located ModuleName, Maybe Import)]
294− -> [(Located ModuleName, Maybe NormalizedFilePath)]
295−fileImports = mapMaybe $ \case
296− (modName, Nothing) -> Just (modName, Nothing)
297− (modName, Just (FileImport absFile)) -> Just (modName, Just absFile)
298− (_modName, Just (PackageImport _pkg)) -> Nothing
src/Development/IDE/Core/Service.hs+45−39
@@ -1,8 +1,7 @@
1−-- Copyright (c) 2019 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
1+-- Copyright (c) 2019 The DAML Authors. All rights reserved.
2-- SPDX-License-Identifier: Apache-2.0
2-- SPDX-License-Identifier: Apache-2.0
3
3
4{-# LANGUAGE TypeFamilies #-}
4{-# LANGUAGE TypeFamilies #-}
5−{-# LANGUAGE OverloadedStrings #-}
6{-# LANGUAGE FlexibleInstances #-}
5{-# LANGUAGE FlexibleInstances #-}
7
6
8-- | A Shake implementation of the compiler service, built
7-- | A Shake implementation of the compiler service, built
@@ -11,21 +10,29 @@
11module Development.IDE.Core.Service(
10module Development.IDE.Core.Service(
12 getIdeOptions,
11 getIdeOptions,
13 IdeState, initialise, shutdown,
12 IdeState, initialise, shutdown,
14− runAction, runActions,
13+ runAction,
15− runActionSync, runActionsSync,
14+ runActionSync,
16 writeProfile,
15 writeProfile,
17 getDiagnostics, unsafeClearDiagnostics,
16 getDiagnostics, unsafeClearDiagnostics,
18− ideLogger
17+ ideLogger,
18+ updatePositionMapping,
19 ) where
19 ) where
20
20
21import Control.Concurrent.Extra
21import Control.Concurrent.Extra
22−import Control.Monad.Except
22+import Control.Concurrent.Async
23+import Data.Maybe
23import Development.IDE.Types.Options (IdeOptions(..))
24import Development.IDE.Types.Options (IdeOptions(..))
25+import Control.Monad
26+import Development.IDE.Core.Debouncer
24−import Development.IDE.Core.FileStore
27+import Development.IDE.Core.FileStore (VFSHandle, fileStoreRules)
28+import Development.IDE.Core.FileExists (fileExistsRules)
25import Development.IDE.Core.OfInterest
29import Development.IDE.Core.OfInterest
26import Development.IDE.Types.Logger
30import Development.IDE.Types.Logger
27−import Development.Shake hiding (Diagnostic, Env, newCache)
31+import Development.Shake
32+import Data.Either.Extra
28import qualified Language.Haskell.LSP.Messages as LSP
33import qualified Language.Haskell.LSP.Messages as LSP
34+import qualified Language.Haskell.LSP.Types as LSP
35+import qualified Language.Haskell.LSP.Types.Capabilities as LSP
29
36
30import Development.IDE.Core.Shake
37import Development.IDE.Core.Shake
31
38
@@ -38,62 +45,61 @@ instance IsIdeGlobal GlobalIdeOptions
38-- Exposed API
45-- Exposed API
39
46
40-- | Initialise the Compiler Service.
47-- | Initialise the Compiler Service.
48+initialise :: LSP.ClientCapabilities
41−initialise :: Rules ()
49+ -> Rules ()
50+ -> IO LSP.LspId
42 -> (LSP.FromServerMessage -> IO ())
51 -> (LSP.FromServerMessage -> IO ())
43 -> Logger
52 -> Logger
53+ -> Debouncer LSP.NormalizedUri
44 -> IdeOptions
54 -> IdeOptions
45 -> VFSHandle
55 -> VFSHandle
46 -> IO IdeState
56 -> IO IdeState
47−initialise mainRule toDiags logger options vfs =
57+initialise caps mainRule getLspId toDiags logger debouncer options vfs =
48 shakeOpen
58 shakeOpen
59+ getLspId
49 toDiags
60 toDiags
50 logger
61 logger
51− (setProfiling options $
62+ debouncer
63+ (optShakeProfiling options)
64+ (optReportProgress options)
65+ shakeOptions
52− shakeOptions { shakeThreads = optThreads options
66+ { shakeThreads = optThreads options
53− , shakeFiles = "/dev/null"
67+ , shakeFiles = fromMaybe "/dev/null" (optShakeFiles options)
54− }) $ do
68+ } $ do
55 addIdeGlobal $ GlobalIdeOptions options
69 addIdeGlobal $ GlobalIdeOptions options
56 fileStoreRules vfs
70 fileStoreRules vfs
57 ofInterestRules
71 ofInterestRules
72+ fileExistsRules getLspId caps vfs
58 mainRule
73 mainRule
59
74
60writeProfile :: IdeState -> FilePath -> IO ()
75writeProfile :: IdeState -> FilePath -> IO ()
61writeProfile = shakeProfile
76writeProfile = shakeProfile
62
77
63−setProfiling :: IdeOptions -> ShakeOptions -> ShakeOptions
64−setProfiling opts shakeOpts =
65− maybe shakeOpts (\p -> shakeOpts { shakeReport = [p], shakeTimings = True }) (optShakeProfiling opts)
66−
67-- | Shutdown the Compiler Service.
78-- | Shutdown the Compiler Service.
68shutdown :: IdeState -> IO ()
79shutdown :: IdeState -> IO ()
69shutdown = shakeShut
80shutdown = shakeShut
70
81
71−-- | Run a single action using the supplied service. See `runActions`
72−-- for more details.
73−runAction :: IdeState -> Action a -> IO a
74−runAction service action = head <$> runActions service [action]
75−
76−-- | Run a list of actions in parallel using the supplied service.
77−-- This will return as soon as the results of the actions are
82+-- This will return as soon as the result of the action is
78-- available. There might still be other rules running at this point,
83-- available. There might still be other rules running at this point,
79-- e.g., the ofInterestRule.
84-- e.g., the ofInterestRule.
80−runActions :: IdeState -> [Action a] -> IO [a]
85+runAction :: IdeState -> Action a -> IO a
81−runActions x acts = do
86+runAction ide action = do
82− var <- newBarrier
87+ bar <- newBarrier
83− _ <- shakeRun x acts (signalBarrier var)
88+ res <- shakeRun ide [do v <- action; liftIO $ signalBarrier bar v; return v]
84− waitBarrier var
89+ -- shakeRun might throw an exception (either through action or a default rule),
85−
90+ -- in which case action may not complete successfully, and signalBarrier might not be called.
86−-- | This is a synchronous variant of `runAction`. See
91+ -- Therefore we wait for either res (which propagates the exception) or the barrier.
87−-- `runActionsSync` of more details.
92+ -- Importantly, if the barrier does finish, cancelling res only kills waiting for the result,
88−runActionSync :: IdeState -> Action a -> IO a
93+ -- it doesn't kill the actual work
89−runActionSync s a = head <$> runActionsSync s [a]
94+ fmap fromEither $ race (head <$> res) $ waitBarrier bar
90−
95+
96+
91−-- | `runActionsSync` is similar to `runActions` but it will
97+-- | `runActionSync` is similar to `runAction` but it will
92-- wait for all rules (so in particular the `ofInterestRule`) to
98-- wait for all rules (so in particular the `ofInterestRule`) to
93-- finish running. This is mainly useful in tests, where you want
99-- finish running. This is mainly useful in tests, where you want
94-- to wait for all rules to fire so you can check diagnostics.
100-- to wait for all rules to fire so you can check diagnostics.
95−runActionsSync :: IdeState -> [Action a] -> IO [a]
101+runActionSync :: IdeState -> Action a -> IO a
96−runActionsSync s acts = join $ shakeRun s acts (const $ pure ())
102+runActionSync s act = fmap head $ join $ shakeRun s [act]
97
103
98getIdeOptions :: Action IdeOptions
104getIdeOptions :: Action IdeOptions
99getIdeOptions = do
105getIdeOptions = do
src/Development/IDE/Core/Shake.hs+544−177
@@ -1,10 +1,9 @@
1−-- Copyright (c) 2019 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
1+-- Copyright (c) 2019 The DAML Authors. All rights reserved.
2-- SPDX-License-Identifier: Apache-2.0
2-- SPDX-License-Identifier: Apache-2.0
3
3
4{-# LANGUAGE ExistentialQuantification #-}
4{-# LANGUAGE ExistentialQuantification #-}
5{-# LANGUAGE TypeFamilies #-}
5{-# LANGUAGE TypeFamilies #-}
6{-# LANGUAGE ConstraintKinds #-}
6{-# LANGUAGE ConstraintKinds #-}
7−{-# LANGUAGE OverloadedStrings #-}
8
7
9-- | A Shake implementation of the compiler service.
8-- | A Shake implementation of the compiler service.
10--
9--
@@ -19,47 +18,58 @@
19-- * The 'Values' type stores a map of keys to values. These values are
18-- * The 'Values' type stores a map of keys to values. These values are
20-- always stored as real Haskell values, whereas Shake serialises all 'A' values
19-- always stored as real Haskell values, whereas Shake serialises all 'A' values
21-- between runs. To deserialise a Shake value, we just consult Values.
20-- between runs. To deserialise a Shake value, we just consult Values.
22−-- Additionally, Values can be used in an inconsistent way, for example
23−-- useStale.
24module Development.IDE.Core.Shake(
21module Development.IDE.Core.Shake(
25− IdeState,
22+ IdeState, shakeExtras,
23+ ShakeExtras(..), getShakeExtras, getShakeExtrasRules,
26 IdeRule, IdeResult, GetModificationTime(..),
24 IdeRule, IdeResult, GetModificationTime(..),
27 shakeOpen, shakeShut,
25 shakeOpen, shakeShut,
28 shakeRun,
26 shakeRun,
29 shakeProfile,
27 shakeProfile,
30− useStale,
28+ use, useWithStale, useNoFile, uses, usesWithStale,
31− use, uses,
32− use_, uses_,
29+ use_, useNoFile_, uses_,
33− define, defineEarlyCutoff,
30+ define, defineEarlyCutoff, defineOnDisk, needOnDisk, needOnDisks,
34 getDiagnostics, unsafeClearDiagnostics,
31 getDiagnostics, unsafeClearDiagnostics,
35− reportSeriousError,
32+ getHiddenDiagnostics,
36− IsIdeGlobal, addIdeGlobal, getIdeGlobalState, getIdeGlobalAction,
33+ IsIdeGlobal, addIdeGlobal, addIdeGlobalExtras, getIdeGlobalState, getIdeGlobalAction,
37 garbageCollect,
34 garbageCollect,
38 setPriority,
35 setPriority,
39 sendEvent,
36 sendEvent,
40 ideLogger,
37 ideLogger,
38+ actionLogger,
41− FileVersion(..)
39+ FileVersion(..), modificationTime,
40+ Priority(..),
41+ updatePositionMapping,
42+ deleteValue,
43+ OnDiskRule(..),
42 ) where
44 ) where
43
45
44−import Development.Shake
46+import Development.Shake hiding (ShakeValue, doesFileExist)
45import Development.Shake.Database
47import Development.Shake.Database
46import Development.Shake.Classes
48import Development.Shake.Classes
47import Development.Shake.Rule
49import Development.Shake.Rule
48import qualified Data.HashMap.Strict as HMap
50import qualified Data.HashMap.Strict as HMap
49−import qualified Data.Map as Map
51+import qualified Data.Map.Strict as Map
50import qualified Data.ByteString.Char8 as BS
52import qualified Data.ByteString.Char8 as BS
51import Data.Dynamic
53import Data.Dynamic
52import Data.Maybe
54import Data.Maybe
53−import Data.Either.Extra
55+import Data.Map.Strict (Map)
54−import Data.List.Extra
56+import Data.List.Extra (partition, takeEnd)
57+import qualified Data.Set as Set
55import qualified Data.Text as T
58import qualified Data.Text as T
59+import Data.Traversable (for)
60+import Data.Tuple.Extra
61+import Data.Unique
62+import Development.IDE.Core.Debouncer
63+import Development.IDE.Core.PositionMapping
56−import Development.IDE.Types.Logger
64+import Development.IDE.Types.Logger hiding (Priority)
57import Language.Haskell.LSP.Diagnostics
65import Language.Haskell.LSP.Diagnostics
58import qualified Data.SortedList as SL
66import qualified Data.SortedList as SL
59import Development.IDE.Types.Diagnostics
67import Development.IDE.Types.Diagnostics
60import Development.IDE.Types.Location
68import Development.IDE.Types.Location
69+import Development.IDE.Types.Options
70+import Control.Concurrent.Async
61import Control.Concurrent.Extra
71import Control.Concurrent.Extra
62−import Control.Exception
72+import Control.Exception.Extra
63import Control.DeepSeq
73import Control.DeepSeq
64import System.Time.Extra
74import System.Time.Extra
65import Data.Typeable
75import Data.Typeable
@@ -72,16 +82,28 @@ import Data.Time
72import GHC.Generics
82import GHC.Generics
73import System.IO.Unsafe
83import System.IO.Unsafe
74import Numeric.Extra
84import Numeric.Extra
75−
85+import Language.Haskell.LSP.Types
76
86
77
87
78-- information we stash inside the shakeExtra field
88-- information we stash inside the shakeExtra field
79data ShakeExtras = ShakeExtras
89data ShakeExtras = ShakeExtras
80 {eventer :: LSP.FromServerMessage -> IO ()
90 {eventer :: LSP.FromServerMessage -> IO ()
91+ ,debouncer :: Debouncer NormalizedUri
81 ,logger :: Logger
92 ,logger :: Logger
82 ,globals :: Var (HMap.HashMap TypeRep Dynamic)
93 ,globals :: Var (HMap.HashMap TypeRep Dynamic)
83 ,state :: Var Values
94 ,state :: Var Values
84 ,diagnostics :: Var DiagnosticStore
95 ,diagnostics :: Var DiagnosticStore
96+ ,hiddenDiagnostics :: Var DiagnosticStore
97+ ,publishedDiagnostics :: Var (HMap.HashMap NormalizedUri [Diagnostic])
98+ -- ^ This represents the set of diagnostics that we have published.
99+ -- Due to debouncing not every change might get published.
100+ ,positionMapping :: Var (HMap.HashMap NormalizedUri (Map TextDocumentVersion (PositionDelta, PositionMapping)))
101+ -- ^ Map from a text document version to a PositionMapping that describes how to map
102+ -- 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
85 }
107 }
86
108
87getShakeExtras :: Action ShakeExtras
109getShakeExtras :: Action ShakeExtras
@@ -94,22 +116,29 @@ getShakeExtrasRules = do
94 Just x <- getShakeExtraRules @ShakeExtras
116 Just x <- getShakeExtraRules @ShakeExtras
95 return x
117 return x
96
118
97−
98−
99class Typeable a => IsIdeGlobal a where
119class Typeable a => IsIdeGlobal a where
100
120
101addIdeGlobal :: IsIdeGlobal a => a -> Rules ()
121addIdeGlobal :: IsIdeGlobal a => a -> Rules ()
102−addIdeGlobal x@(typeOf -> ty) = do
122+addIdeGlobal x = do
123+ extras <- getShakeExtrasRules
124+ liftIO $ addIdeGlobalExtras extras x
125+
126+addIdeGlobalExtras :: IsIdeGlobal a => ShakeExtras -> a -> IO ()
103− ShakeExtras{globals} <- getShakeExtrasRules
127+addIdeGlobalExtras ShakeExtras{globals} x@(typeOf -> ty) =
104 liftIO $ modifyVar_ globals $ \mp -> case HMap.lookup ty mp of
128 liftIO $ modifyVar_ globals $ \mp -> case HMap.lookup ty mp of
105− Just _ -> error $ "Can't addIdeGlobal twice on the same type, got " ++ show ty
129+ Just _ -> errorIO $ "Internal error, addIdeGlobalExtras, got the same type twice for " ++ show ty
106 Nothing -> return $! HMap.insert ty (toDyn x) mp
130 Nothing -> return $! HMap.insert ty (toDyn x) mp
107
131
108
132
109getIdeGlobalExtras :: forall a . IsIdeGlobal a => ShakeExtras -> IO a
133getIdeGlobalExtras :: forall a . IsIdeGlobal a => ShakeExtras -> IO a
110getIdeGlobalExtras ShakeExtras{globals} = do
134getIdeGlobalExtras ShakeExtras{globals} = do
135+ let typ = typeRep (Proxy :: Proxy a)
111− Just x <- HMap.lookup (typeRep (Proxy :: Proxy a)) <$> readVar globals
136+ x <- HMap.lookup (typeRep (Proxy :: Proxy a)) <$> readVar globals
112− return $ fromDyn x $ error "Serious error, corrupt 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
113
142
114getIdeGlobalAction :: forall a . IsIdeGlobal a => Action a
143getIdeGlobalAction :: forall a . IsIdeGlobal a => Action a
115getIdeGlobalAction = liftIO . getIdeGlobalExtras =<< getShakeExtras
144getIdeGlobalAction = liftIO . getIdeGlobalExtras =<< getShakeExtras
@@ -118,8 +147,8 @@ getIdeGlobalState :: forall a . IsIdeGlobal a => IdeState -> IO a
118getIdeGlobalState = getIdeGlobalExtras . shakeExtras
147getIdeGlobalState = getIdeGlobalExtras . shakeExtras
119
148
120
149
121−-- | The state of the all values - nested so you can easily find all errors at a given file.
150+-- | The state of the all values.
122−type Values = HMap.HashMap (NormalizedFilePath, Key) (Maybe Dynamic)
151+type Values = HMap.HashMap (NormalizedFilePath, Key) (Value Dynamic)
123
152
124-- | Key type
153-- | Key type
125data Key = forall k . (Typeable k, Hashable k, Eq k, Show k) => Key k
154data Key = forall k . (Typeable k, Hashable k, Eq k, Show k) => Key k
@@ -132,7 +161,7 @@ instance Eq Key where
132 | otherwise = False
161 | otherwise = False
133
162
134instance Hashable Key where
163instance Hashable Key where
135− hashWithSalt salt (Key key) = hashWithSalt salt key
164+ hashWithSalt salt (Key key) = hashWithSalt salt (typeOf key, key)
136
165
137-- | The result of an IDE operation. Warnings and errors are in the Diagnostic,
166-- | The result of an IDE operation. Warnings and errors are in the Diagnostic,
138-- and a value is in the Maybe. For operations that throw an error you
167-- and a value is in the Maybe. For operations that throw an error you
@@ -142,46 +171,78 @@ instance Hashable Key where
142-- get empty diagnostics and a Nothing, to indicate this phase throws no fresh
171-- get empty diagnostics and a Nothing, to indicate this phase throws no fresh
143-- errors but still failed.
172-- errors but still failed.
144--
173--
145−-- A rule on a file should only return diagnostics for that given file. It should
174+
146−-- not propagate diagnostic errors through multiple phases.
175+data Value v
147−type IdeResult v = ([FileDiagnostic], Maybe v)
176+ = Succeeded TextDocumentVersion v
177+ | Stale TextDocumentVersion v
178+ | Failed
179+ deriving (Functor, Generic, Show)
180+
181+instance NFData v => NFData (Value v)
182+
183+-- | Convert a Value to a Maybe. This will only return `Just` for
184+-- up2date results not for stale values.
185+currentValue :: Value v -> Maybe v
186+currentValue (Succeeded _ v) = Just v
187+currentValue (Stale _ _) = Nothing
188+currentValue Failed = Nothing
189+
190+-- | Return the most recent, potentially stale, value and a PositionMapping
191+-- for the version of that value.
192+lastValue :: NormalizedFilePath -> Value v -> Action (Maybe (v, PositionMapping))
193+lastValue file v = do
194+ ShakeExtras{positionMapping} <- getShakeExtras
195+ allMappings <- liftIO $ readVar positionMapping
196+ pure $ case v of
197+ Succeeded ver v -> Just (v, mappingForVersion allMappings file ver)
198+ Stale ver v -> Just (v, mappingForVersion allMappings file ver)
199+ Failed -> Nothing
200+
201+valueVersion :: Value v -> Maybe TextDocumentVersion
202+valueVersion = \case
203+ Succeeded ver _ -> Just ver
204+ Stale ver _ -> Just ver
205+ Failed -> Nothing
206+
207+mappingForVersion
208+ :: HMap.HashMap NormalizedUri (Map TextDocumentVersion (a, PositionMapping))
209+ -> NormalizedFilePath
210+ -> TextDocumentVersion
211+ -> PositionMapping
212+mappingForVersion allMappings file ver =
213+ maybe zeroMapping snd $
214+ Map.lookup ver =<<
215+ HMap.lookup (filePathToUri' file) allMappings
148
216
149type IdeRule k v =
217type IdeRule k v =
150 ( Shake.RuleResult k ~ v
218 ( Shake.RuleResult k ~ v
151− , Show k
219+ , Shake.ShakeValue k
152− , Typeable k
153− , NFData k
154− , Hashable k
155− , Eq k
156 , Show v
220 , Show v
157 , Typeable v
221 , Typeable v
158 , NFData v
222 , NFData v
159 )
223 )
160
224
161−
162-- | A Shake database plus persistent store. Can be thought of as storing
225-- | A Shake database plus persistent store. Can be thought of as storing
163-- mappings from @(FilePath, k)@ to @RuleResult k@.
226-- mappings from @(FilePath, k)@ to @RuleResult k@.
164data IdeState = IdeState
227data IdeState = IdeState
165 {shakeDb :: ShakeDatabase
228 {shakeDb :: ShakeDatabase
166− ,shakeAbort :: Var (IO ()) -- close whoever was running last
229+ ,shakeAbort :: MVar (IO ()) -- close whoever was running last
167 ,shakeClose :: IO ()
230 ,shakeClose :: IO ()
168 ,shakeExtras :: ShakeExtras
231 ,shakeExtras :: ShakeExtras
232+ ,shakeProfileDir :: Maybe FilePath
169 }
233 }
170
234
171−profileDir :: Maybe FilePath
172−profileDir = Nothing -- set to Just the directory you want profile reports to appear in
173−
174
235
175-- This is debugging code that generates a series of profiles, if the Boolean is true
236-- This is debugging code that generates a series of profiles, if the Boolean is true
176−shakeRunDatabaseProfile :: ShakeDatabase -> [Action a] -> IO [a]
237+shakeRunDatabaseProfile :: Maybe FilePath -> ShakeDatabase -> [Action a] -> IO ([a], Maybe FilePath)
177−shakeRunDatabaseProfile shakeDb acts = do
238+shakeRunDatabaseProfile mbProfileDir shakeDb acts = do
178 (time, (res,_)) <- duration $ shakeRunDatabase shakeDb acts
239 (time, (res,_)) <- duration $ shakeRunDatabase shakeDb acts
179− whenJust profileDir $ \dir -> do
240+ proFile <- for mbProfileDir $ \dir -> do
180− count <- modifyVar profileCounter $ \x -> let y = x+1 in return (y,y)
241+ count <- modifyVar profileCounter $ \x -> let !y = x+1 in return (y,y)
181− let file = "ide-" ++ profileStartTime ++ "-" ++ takeEnd 5 ("0000" ++ show count) ++ "-" ++ showDP 2 time <.> "html"
242+ let file = "ide-" ++ profileStartTime ++ "-" ++ takeEnd 5 ("0000" ++ show count) ++ "-" ++ showDP 2 time <.> "html"
182− shakeProfileDatabase shakeDb $ dir </> file
243+ shakeProfileDatabase shakeDb $ dir </> file
244+ return (dir </> file)
183− return res
245+ return (res, proFile)
184− where
185
246
186{-# NOINLINE profileStartTime #-}
247{-# NOINLINE profileStartTime #-}
187profileStartTime :: String
248profileStartTime :: String
@@ -195,86 +256,191 @@ setValues :: IdeRule k v
195 => Var Values
256 => Var Values
196 -> k
257 -> k
197 -> NormalizedFilePath
258 -> NormalizedFilePath
198− -> Maybe v
259+ -> Value v
199 -> IO ()
260 -> IO ()
200−setValues state key file val = modifyVar_ state $
261+setValues state key file val = modifyVar_ state $ \vals -> do
262+ -- Force to make sure the old HashMap is not retained
201− pure . HMap.insert (file, Key key) (fmap toDyn val)
263+ evaluate $ HMap.insert (file, Key key) (fmap toDyn val) vals
202−
264+
203−-- | The outer Maybe is Nothing if this function hasn't been computed before
265+-- | Delete the value stored for a given ide build key
204−-- the inner Maybe is Nothing if the result of the previous computation failed to produce
266+deleteValue
205−-- a value
267+ :: (Typeable k, Hashable k, Eq k, Show k)
268+ => IdeState
269+ -> k
270+ -> NormalizedFilePath
271+ -> IO ()
272+deleteValue IdeState{shakeExtras = ShakeExtras{state}} key file = modifyVar_ state $ \vals ->
273+ evaluate $ HMap.delete (file, Key key) vals
274+
275+-- | We return Nothing if the rule has not run and Just Failed if it has failed to produce a value.
206−getValues :: forall k v. IdeRule k v => Var Values -> k -> NormalizedFilePath -> IO (Maybe (Maybe v))
276+getValues :: forall k v. IdeRule k v => Var Values -> k -> NormalizedFilePath -> IO (Maybe (Value v))
207getValues state key file = do
277getValues state key file = do
208 vs <- readVar state
278 vs <- readVar state
209− return $ do
210− v <- HMap.lookup (file, Key key) vs
279+ case HMap.lookup (file, Key key) vs of
280+ Nothing -> pure Nothing
281+ Just v -> do
211− pure $ fmap (fromJust . fromDynamic @v) v
282+ let r = fmap (fromJust . fromDynamic @v) v
283+ -- Force to make sure we do not retain a reference to the HashMap
284+ -- and we blow up immediately if the fromJust should fail
285+ -- (which would be an internal error).
286+ evaluate (r `seqValue` Just r)
287+
288+-- | Seq the result stored in the Shake value. This only
289+-- evaluates the value to WHNF not NF. We take care of the latter
290+-- elsewhere and doing it twice is expensive.
291+seqValue :: Value v -> b -> b
292+seqValue v b = case v of
293+ Succeeded ver v -> rnf ver `seq` v `seq` b
294+ Stale ver v -> rnf ver `seq` v `seq` b
295+ Failed -> b
212
296
213-- | Open a 'IdeState', should be shut using 'shakeShut'.
297-- | Open a 'IdeState', should be shut using 'shakeShut'.
298+shakeOpen :: IO LSP.LspId
214−shakeOpen :: (LSP.FromServerMessage -> IO ()) -- ^ diagnostic handler
299+ -> (LSP.FromServerMessage -> IO ()) -- ^ diagnostic handler
215 -> Logger
300 -> Logger
301+ -> Debouncer NormalizedUri
302+ -> Maybe FilePath
303+ -> IdeReportProgress
216 -> ShakeOptions
304 -> ShakeOptions
217 -> Rules ()
305 -> Rules ()
218 -> IO IdeState
306 -> IO IdeState
219−shakeOpen eventer logger opts rules = do
307+shakeOpen getLspId eventer logger debouncer shakeProfileDir (IdeReportProgress reportProgress) opts rules = do
308+ inProgress <- newVar HMap.empty
220 shakeExtras <- do
309 shakeExtras <- do
221 globals <- newVar HMap.empty
310 globals <- newVar HMap.empty
222 state <- newVar HMap.empty
311 state <- newVar HMap.empty
223 diagnostics <- newVar mempty
312 diagnostics <- newVar mempty
313+ hiddenDiagnostics <- newVar mempty
314+ publishedDiagnostics <- newVar mempty
315+ positionMapping <- newVar HMap.empty
224 pure ShakeExtras{..}
316 pure ShakeExtras{..}
317+ (shakeDb, shakeClose) <-
318+ shakeOpenDatabase
319+ opts
225− (shakeDb, shakeClose) <- shakeOpenDatabase opts{shakeExtra = addShakeExtra shakeExtras $ shakeExtra opts} rules
320+ { 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 ()
324+ }
325+ rules
226− shakeAbort <- newVar $ return ()
326+ shakeAbort <- newMVar $ return ()
227 shakeDb <- shakeDb
327 shakeDb <- shakeDb
228 return IdeState{..}
328 return IdeState{..}
229
329
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
335+ lspId <- getLspId
336+ u <- ProgressTextToken . T.pack . show . hashUnique <$> newUnique
337+ sendMsg $ LSP.ReqWorkDoneProgressCreate $ LSP.fmServerWorkDoneProgressCreateRequest
338+ lspId $ LSP.WorkDoneProgressCreateParams
339+ { _token = u }
340+ bracket_ (start u) (stop u) (loop u Nothing)
341+ where
342+ start id = sendMsg $ LSP.NotWorkDoneProgressBegin $ LSP.fmServerWorkDoneProgressBeginNotification
343+ LSP.ProgressParams
344+ { _token = id
345+ , _value = WorkDoneProgressBeginParams
346+ { _title = "Processing"
347+ , _cancellable = Nothing
348+ , _message = Nothing
349+ , _percentage = Nothing
350+ }
351+ }
352+ stop id = sendMsg $ LSP.NotWorkDoneProgressEnd $ LSP.fmServerWorkDoneProgressEndNotification
353+ LSP.ProgressParams
354+ { _token = id
355+ , _value = WorkDoneProgressEndParams
356+ { _message = Nothing
357+ }
358+ }
359+ sample = 0.1
360+ loop id prev = do
361+ sleep sample
362+ current <- readVar inProgress
363+ let done = length $ filter (== 0) $ HMap.elems current
364+ let todo = HMap.size current
365+ let next = Just $ T.pack $ show done <> "/" <> show todo
366+ when (next /= prev) $
367+ sendMsg $ LSP.NotWorkDoneProgressReport $ LSP.fmServerWorkDoneProgressReportNotification
368+ LSP.ProgressParams
369+ { _token = id
370+ , _value = LSP.WorkDoneProgressReportParams
371+ { _cancellable = Nothing
372+ , _message = next
373+ , _percentage = Nothing
374+ }
375+ }
376+ loop id next
377+
230shakeProfile :: IdeState -> FilePath -> IO ()
378shakeProfile :: IdeState -> FilePath -> IO ()
231shakeProfile IdeState{..} = shakeProfileDatabase shakeDb
379shakeProfile IdeState{..} = shakeProfileDatabase shakeDb
232
380
233shakeShut :: IdeState -> IO ()
381shakeShut :: IdeState -> IO ()
234−shakeShut IdeState{..} = withVar shakeAbort $ \stop -> do
382+shakeShut IdeState{..} = withMVar shakeAbort $ \stop -> do
235 -- Shake gets unhappy if you try to close when there is a running
383 -- Shake gets unhappy if you try to close when there is a running
236 -- request so we first abort that.
384 -- request so we first abort that.
237 stop
385 stop
238 shakeClose
386 shakeClose
239
387
240−-- | Spawn immediately, add an action to collect the results syncronously.
388+-- | This is a variant of withMVar where the first argument is run unmasked and if it throws
389+-- an exception, the previous value is restored while the second argument is executed masked.
390+withMVar' :: MVar a -> (a -> IO ()) -> IO (a, c) -> IO c
391+withMVar' var unmasked masked = mask $ \restore -> do
392+ a <- takeMVar var
393+ restore (unmasked a) `onException` putMVar var a
394+ (a', c) <- masked
395+ putMVar var a'
396+ pure c
397+
241−-- If you are already inside a call to shakeRun that will be aborted with an exception.
398+-- | Spawn immediately. If you are already inside a call to shakeRun that will be aborted with an exception.
242−-- The callback will be fired as soon as the results are available
243−-- even if there are still other rules running while the IO action that is
244−-- being returned will wait for all rules to finish.
245−shakeRun :: IdeState -> [Action a] -> ([a] -> IO ()) -> IO (IO [a])
399+shakeRun :: IdeState -> [Action a] -> IO (IO [a])
246−-- FIXME: If there is already a shakeRun queued up and waiting to send me a kill, I should probably
247−-- not even start, which would make issues with async exceptions less problematic.
248−shakeRun IdeState{shakeExtras=ShakeExtras{..}, ..} acts callback = modifyVar shakeAbort $ \stop -> do
400+shakeRun IdeState{shakeExtras=ShakeExtras{..}, ..} acts =
401+ withMVar'
402+ shakeAbort
403+ (\stop -> do
249− (stopTime,_) <- duration stop
404+ (stopTime,_) <- duration stop
250− logDebug logger $ T.pack $ "Starting shakeRun (aborting the previous one took " ++ showDuration stopTime ++ ")"
405+ logDebug logger $ T.pack $ "Starting shakeRun (aborting the previous one took " ++ showDuration stopTime ++ ")"
251− bar <- newBarrier
406+ )
407+ -- It is crucial to be masked here, otherwise we can get killed
408+ -- between spawning the new thread and updating shakeAbort.
409+ -- See https://github.com/digital-asset/ghcide/issues/79
410+ (do
252− start <- offsetTime
411+ start <- offsetTime
253− let act = do
412+ aThread <- asyncWithUnmask $ \restore -> do
254− res <- parallel acts
255− liftIO $ callback res
256− pure res
257− thread <- forkFinally (shakeRunDatabaseProfile shakeDb [act]) $ \res -> do
413+ res <- try (restore $ shakeRunDatabaseProfile shakeProfileDir shakeDb acts)
258− signalBarrier bar (mapRight head res)
259− runTime <- start
414+ runTime <- start
260− logDebug logger $ T.pack $
415+ let res' = case res of
261− "Finishing shakeRun (took " ++ showDuration runTime ++ ", " ++ (if isLeft res then "exception" else "completed") ++ ")"
416+ Left e -> "exception: " <> displayException e
262− -- important: we send an async exception to the thread, then wait for it to die, before continuing
417+ Right _ -> "completed"
263− return (do killThread thread; void $ waitBarrier bar, either throwIO return =<< waitBarrier bar)
418+ profile = case res of
264−
419+ Right (_, Just fp) ->
265−-- | Use the last stale value, if it's ever been computed.
420+ let link = case filePathToUri' $ toNormalizedFilePath' fp of
266−useStale
421+ NormalizedUri _ x -> x
267− :: IdeRule k v
422+ in ", profile saved at " <> T.unpack link
268− => IdeState -> k -> NormalizedFilePath -> IO (Maybe v)
423+ _ -> ""
269−useStale IdeState{shakeExtras=ShakeExtras{state}} k fp =
424+ let logMsg = logDebug logger $ T.pack $
270− join <$> getValues state k fp
425+ "Finishing shakeRun (took " ++ showDuration runTime ++ ", " ++ res' ++ profile ++ ")"
271−
426+ return (fst <$> res, logMsg)
427+ let wrapUp (res, _) = do
428+ either (throwIO @SomeException) return res
429+ _ <- async $ do
430+ (_, logMsg) <- wait aThread
431+ logMsg
432+ pure (cancel aThread, wrapUp =<< wait aThread))
272
433
273getDiagnostics :: IdeState -> IO [FileDiagnostic]
434getDiagnostics :: IdeState -> IO [FileDiagnostic]
274getDiagnostics IdeState{shakeExtras = ShakeExtras{diagnostics}} = do
435getDiagnostics IdeState{shakeExtras = ShakeExtras{diagnostics}} = do
275 val <- readVar diagnostics
436 val <- readVar diagnostics
276 return $ getAllDiagnostics val
437 return $ getAllDiagnostics val
277
438
439+getHiddenDiagnostics :: IdeState -> IO [FileDiagnostic]
440+getHiddenDiagnostics IdeState{shakeExtras = ShakeExtras{hiddenDiagnostics}} = do
441+ val <- readVar hiddenDiagnostics
442+ return $ getAllDiagnostics val
443+
278-- | FIXME: This function is temporary! Only required because the files of interest doesn't work
444-- | FIXME: This function is temporary! Only required because the files of interest doesn't work
279unsafeClearDiagnostics :: IdeState -> IO ()
445unsafeClearDiagnostics :: IdeState -> IO ()
280unsafeClearDiagnostics IdeState{shakeExtras = ShakeExtras{diagnostics}} =
446unsafeClearDiagnostics IdeState{shakeExtras = ShakeExtras{diagnostics}} =
@@ -283,11 +449,19 @@ unsafeClearDiagnostics IdeState{shakeExtras = ShakeExtras{diagnostics}} =
283-- | Clear the results for all files that do not match the given predicate.
449-- | Clear the results for all files that do not match the given predicate.
284garbageCollect :: (NormalizedFilePath -> Bool) -> Action ()
450garbageCollect :: (NormalizedFilePath -> Bool) -> Action ()
285garbageCollect keep = do
451garbageCollect keep = do
286− ShakeExtras{state, diagnostics} <- getShakeExtras
452+ ShakeExtras{state, diagnostics,hiddenDiagnostics,publishedDiagnostics,positionMapping} <- getShakeExtras
287 liftIO $
453 liftIO $
454+ do newState <- modifyVar state $ \values -> do
288− do modifyVar_ state $ return . HMap.filterWithKey (\(file, _) _ -> keep file)
455+ values <- evaluate $ HMap.filterWithKey (\(file, _) _ -> keep file) values
289− modifyVar_ diagnostics $ return . filterDiagnostics keep
456+ return $! dupe values
290−
457+ modifyVar_ diagnostics $ \diags -> return $! filterDiagnostics keep diags
458+ modifyVar_ hiddenDiagnostics $ \hdiags -> return $! filterDiagnostics keep hdiags
459+ modifyVar_ publishedDiagnostics $ \diags -> return $! HMap.filterWithKey (\uri _ -> keep (fromUri uri)) diags
460+ let versionsForFile =
461+ HMap.fromListWith Set.union $
462+ mapMaybe (\((file, _key), v) -> (filePathToUri' file,) . Set.singleton <$> valueVersion v) $
463+ HMap.toList newState
464+ modifyVar_ positionMapping $ \mappings -> return $! filterVersionMap versionsForFile mappings
291define
465define
292 :: IdeRule k v
466 :: IdeRule k v
293 => (k -> NormalizedFilePath -> Action (IdeResult v)) -> Rules ()
467 => (k -> NormalizedFilePath -> Action (IdeResult v)) -> Rules ()
@@ -297,25 +471,30 @@ use :: IdeRule k v
297 => k -> NormalizedFilePath -> Action (Maybe v)
471 => k -> NormalizedFilePath -> Action (Maybe v)
298use key file = head <$> uses key [file]
472use key file = head <$> uses key [file]
299
473
474+useWithStale :: IdeRule k v
475+ => k -> NormalizedFilePath -> Action (Maybe (v, PositionMapping))
476+useWithStale key file = head <$> usesWithStale key [file]
477+
478+useNoFile :: IdeRule k v => k -> Action (Maybe v)
479+useNoFile key = use key emptyFilePath
480+
300use_ :: IdeRule k v => k -> NormalizedFilePath -> Action v
481use_ :: IdeRule k v => k -> NormalizedFilePath -> Action v
301use_ key file = head <$> uses_ key [file]
482use_ key file = head <$> uses_ key [file]
302
483
484+useNoFile_ :: IdeRule k v => k -> Action v
485+useNoFile_ key = use_ key emptyFilePath
486+
303uses_ :: IdeRule k v => k -> [NormalizedFilePath] -> Action [v]
487uses_ :: IdeRule k v => k -> [NormalizedFilePath] -> Action [v]
304uses_ key files = do
488uses_ key files = do
305 res <- uses key files
489 res <- uses key files
306 case sequence res of
490 case sequence res of
307− Nothing -> liftIO $ throwIO BadDependency
491+ Nothing -> liftIO $ throwIO $ BadDependency (show key)
308 Just v -> return v
492 Just v -> return v
309
493
310−reportSeriousError :: String -> Action ()
311−reportSeriousError t = do
312− ShakeExtras{logger} <- getShakeExtras
313− liftIO $ logSeriousError logger $ T.pack t
314−
315
494
316-- | When we depend on something that reported an error, and we fail as a direct result, throw BadDependency
495-- | When we depend on something that reported an error, and we fail as a direct result, throw BadDependency
317-- which short-circuits the rest of the action
496-- which short-circuits the rest of the action
318−data BadDependency = BadDependency deriving Show
497+data BadDependency = BadDependency String deriving Show
319instance Exception BadDependency
498instance Exception BadDependency
320
499
321isBadDependency :: SomeException -> Bool
500isBadDependency :: SomeException -> Bool
@@ -325,92 +504,246 @@ isBadDependency x
325 | otherwise = False
504 | otherwise = False
326
505
327newtype Q k = Q (k, NormalizedFilePath)
506newtype Q k = Q (k, NormalizedFilePath)
328− deriving (Eq,Hashable,NFData)
507+ deriving (Eq,Hashable,NFData, Generic)
329
508
330−-- Using Database we don't need Binary instances for keys
331−instance Binary (Q k) where
509+instance Binary k => Binary (Q k)
332− put _ = return ()
333− get = fail "Binary.get not defined for type Development.IDE.Core.Shake.Q"
334
510
335instance Show k => Show (Q k) where
511instance Show k => Show (Q k) where
336 show (Q (k, file)) = show k ++ "; " ++ fromNormalizedFilePath file
512 show (Q (k, file)) = show k ++ "; " ++ fromNormalizedFilePath file
337
513
338-- | Invariant: the 'v' must be in normal form (fully evaluated).
514-- | Invariant: the 'v' must be in normal form (fully evaluated).
339-- Otherwise we keep repeatedly 'rnf'ing values taken from the Shake database
515-- Otherwise we keep repeatedly 'rnf'ing values taken from the Shake database
340−data A v = A (Maybe v) (Maybe BS.ByteString)
516+newtype A v = A (Value v)
341 deriving Show
517 deriving Show
342
518
343−instance NFData (A v) where rnf (A v x) = v `seq` rnf x
519+instance NFData (A v) where rnf (A v) = v `seq` ()
344
520
345-- In the Shake database we only store one type of key/result pairs,
521-- In the Shake database we only store one type of key/result pairs,
346-- namely Q (question) / A (answer).
522-- namely Q (question) / A (answer).
347type instance RuleResult (Q k) = A (RuleResult k)
523type instance RuleResult (Q k) = A (RuleResult k)
348
524
349
525
350−-- | Compute the value
526+-- | Return up2date results. Stale results will be ignored.
351uses :: IdeRule k v
527uses :: IdeRule k v
352 => k -> [NormalizedFilePath] -> Action [Maybe v]
528 => k -> [NormalizedFilePath] -> Action [Maybe v]
529+uses key files = map (\(A value) -> currentValue value) <$> apply (map (Q . (key,)) files)
530+
531+-- | Return the last computed result which might be stale.
532+usesWithStale :: IdeRule k v
533+ => k -> [NormalizedFilePath] -> Action [Maybe (v, PositionMapping)]
534+usesWithStale key files = do
353−uses key files = map (\(A value _) -> value) <$> apply (map (Q . (key,)) files)
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+
354
546
355defineEarlyCutoff
547defineEarlyCutoff
356 :: IdeRule k v
548 :: IdeRule k v
357 => (k -> NormalizedFilePath -> Action (Maybe BS.ByteString, IdeResult v))
549 => (k -> NormalizedFilePath -> Action (Maybe BS.ByteString, IdeResult v))
358 -> Rules ()
550 -> Rules ()
359−defineEarlyCutoff op = addBuiltinRule noLint noIdentity $ \(Q (key, file)) old mode -> do
551+defineEarlyCutoff op = addBuiltinRule noLint noIdentity $ \(Q (key, file)) (old :: Maybe BS.ByteString) mode -> do
360− extras@ShakeExtras{state} <- getShakeExtras
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
361− val <- case old of
555+ val <- case old of
362− Just old | mode == RunDependenciesSame -> do
556+ Just old | mode == RunDependenciesSame -> do
363− v <- liftIO $ getValues state key file
557+ v <- liftIO $ getValues state key file
364− case v of
558+ case v of
559+ -- No changes in the dependencies and we have
560+ -- an existing result.
365− Just v -> return $ Just $ RunResult ChangedNothing old $ A v (unwrap old)
561+ Just v -> return $ Just $ RunResult ChangedNothing old $ A v
366− _ -> return Nothing
562+ _ -> return Nothing
367− _ -> return Nothing
563+ _ -> return Nothing
368− case val of
564+ case val of
369− Just res -> return res
565+ Just res -> return res
370− Nothing -> do
566+ Nothing -> do
371− (bs, (diags, res)) <- actionCatch
567+ (bs, (diags, res)) <- actionCatch
372− (do v <- op key file; liftIO $ evaluate $ force v) $
568+ (do v <- op key file; liftIO $ evaluate $ force v) $
373− \(e :: SomeException) -> pure (Nothing, ([ideErrorText file $ T.pack $ show e | not $ isBadDependency e],Nothing))
569+ \(e :: SomeException) -> pure (Nothing, ([ideErrorText file $ T.pack $ show e | not $ isBadDependency e],Nothing))
374−
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)
375− liftIO $ setValues state key file res
581+ liftIO $ setValues state key file res
376− updateFileDiagnostics file (Key key) extras $ map snd diags
582+ updateFileDiagnostics file (Key key) extras $ map (\(_,y,z) -> (y,z)) diags
377− let eq = case (bs, fmap unwrap old) of
583+ let eq = case (bs, fmap decodeShakeValue old) of
378− (Just a, Just (Just b)) -> a == b
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.
379− _ -> False
588+ _ -> False
380− return $ RunResult
589+ return $ RunResult
381− (if eq then ChangedRecomputeSame else ChangedRecomputeDiff)
590+ (if eq then ChangedRecomputeSame else ChangedRecomputeDiff)
382− (wrap bs)
591+ (encodeShakeValue bs) $
383− $ A res bs
592+ A res
384− where
593+
385− wrap = maybe BS.empty (BS.cons '_')
594+
595+-- | Rule type, input file
596+data QDisk k = QDisk k NormalizedFilePath
597+ deriving (Eq, Generic)
598+
599+instance Hashable k => Hashable (QDisk k)
600+
601+instance NFData k => NFData (QDisk k)
602+
603+instance Binary k => Binary (QDisk k)
604+
605+instance Show k => Show (QDisk k) where
606+ show (QDisk k file) =
607+ show k ++ "; " ++ fromNormalizedFilePath file
608+
609+type instance RuleResult (QDisk k) = Bool
610+
611+data OnDiskRule = OnDiskRule
612+ { getHash :: Action BS.ByteString
613+ -- This is used to figure out if the state on disk corresponds to the state in the Shake
614+ -- database and we can therefore avoid rerunning. Often this can just be the file hash but
615+ -- in some cases we can be more aggressive, e.g., for GHC interface files this can be the ABI hash which
616+ -- is more stable than the hash of the interface file.
617+ -- An empty bytestring indicates that the state on disk is invalid, e.g., files are missing.
618+ -- We do not use a Maybe since we have to deal with encoding things into a ByteString anyway in the Shake DB.
619+ , runRule :: Action (IdeResult BS.ByteString)
620+ -- The actual rule code which produces the new hash (or Nothing if the rule failed) and the diagnostics.
621+ }
622+
623+-- This is used by the DAML compiler for incremental builds. Right now this is not used by
624+-- ghcide itself but that might change in the future.
625+-- The reason why this code lives in ghcide and in particular in this module is that it depends quite heavily on
626+-- the internals of this module that we do not want to expose.
627+defineOnDisk
628+ :: (Shake.ShakeValue k, RuleResult k ~ ())
629+ => (k -> NormalizedFilePath -> OnDiskRule)
630+ -> Rules ()
631+defineOnDisk act = addBuiltinRule noLint noIdentity $
632+ \(QDisk key file) (mbOld :: Maybe BS.ByteString) mode -> do
633+ extras <- getShakeExtras
634+ let OnDiskRule{..} = act key file
635+ let validateHash h
386− unwrap x = if BS.null x then Nothing else Just $ BS.tail x
636+ | BS.null h = Nothing
637+ | otherwise = Just h
638+ let runAct = actionCatch runRule $
639+ \(e :: SomeException) -> pure ([ideErrorText file $ T.pack $ displayException e | not $ isBadDependency e], Nothing)
640+ case mbOld of
641+ Nothing -> do
642+ (diags, mbHash) <- runAct
643+ updateFileDiagnostics file (Key key) extras $ map (\(_,y,z) -> (y,z)) diags
644+ pure $ RunResult ChangedRecomputeDiff (fromMaybe "" mbHash) (isJust mbHash)
645+ Just old -> do
646+ current <- validateHash <$> (actionCatch getHash $ \(_ :: SomeException) -> pure "")
647+ if mode == RunDependenciesSame && Just old == current && not (BS.null old)
648+ then
649+ -- None of our dependencies changed, we’ve had a successful run before and
650+ -- the state on disk matches the state in the Shake database.
651+ pure $ RunResult ChangedNothing (fromMaybe "" current) (isJust current)
652+ else do
653+ (diags, mbHash) <- runAct
654+ updateFileDiagnostics file (Key key) extras $ map (\(_,y,z) -> (y,z)) diags
655+ let change
656+ | mbHash == Just old = ChangedRecomputeSame
657+ | otherwise = ChangedRecomputeDiff
658+ pure $ RunResult change (fromMaybe "" mbHash) (isJust mbHash)
659+
660+needOnDisk :: (Shake.ShakeValue k, RuleResult k ~ ()) => k -> NormalizedFilePath -> Action ()
661+needOnDisk k file = do
662+ successfull <- apply1 (QDisk k file)
663+ liftIO $ unless successfull $ throwIO $ BadDependency (show k)
664+
665+needOnDisks :: (Shake.ShakeValue k, RuleResult k ~ ()) => k -> [NormalizedFilePath] -> Action ()
666+needOnDisks k files = do
667+ successfulls <- apply $ map (QDisk k) files
668+ liftIO $ unless (and successfulls) $ throwIO $ BadDependency (show k)
669+
670+toShakeValue :: (BS.ByteString -> ShakeValue) -> Maybe BS.ByteString -> ShakeValue
671+toShakeValue = maybe ShakeNoCutoff
672+
673+data ShakeValue
674+ = ShakeNoCutoff
675+ -- ^ This is what we use when we get Nothing from
676+ -- a rule.
677+ | ShakeResult !BS.ByteString
678+ -- ^ This is used both for `Failed`
679+ -- as well as `Succeeded`.
680+ | ShakeStale !BS.ByteString
681+ deriving (Generic, Show)
682+
683+instance NFData ShakeValue
684+
685+encodeShakeValue :: ShakeValue -> BS.ByteString
686+encodeShakeValue = \case
687+ ShakeNoCutoff -> BS.empty
688+ ShakeResult r -> BS.cons 'r' r
689+ ShakeStale r -> BS.cons 's' r
690+
691+decodeShakeValue :: BS.ByteString -> ShakeValue
692+decodeShakeValue bs = case BS.uncons bs of
693+ Nothing -> ShakeNoCutoff
694+ Just (x, xs)
695+ | x == 'r' -> ShakeResult xs
696+ | x == 's' -> ShakeStale xs
697+ | otherwise -> error $ "Failed to parse shake value " <> show bs
698+
387
699
388updateFileDiagnostics ::
700updateFileDiagnostics ::
389 NormalizedFilePath
701 NormalizedFilePath
390 -> Key
702 -> Key
391 -> ShakeExtras
703 -> ShakeExtras
392− -> [Diagnostic] -- ^ current results
704+ -> [(ShowDiagnostic,Diagnostic)] -- ^ current results
393 -> Action ()
705 -> Action ()
394−updateFileDiagnostics fp k ShakeExtras{diagnostics, state} current = do
706+updateFileDiagnostics fp k ShakeExtras{diagnostics, hiddenDiagnostics, publishedDiagnostics, state, debouncer, eventer} current = liftIO $ do
395− (newDiags, oldDiags) <- liftIO $ do
396− modTime <- join <$> getValues state GetModificationTime fp
707+ modTime <- (currentValue =<<) <$> getValues state GetModificationTime fp
708+ let (currentShown, currentHidden) = partition ((== ShowDiag) . fst) current
709+ mask_ $ do
710+ -- Mask async exceptions to ensure that updated diagnostics are always
711+ -- published. Otherwise, we might never publish certain diagnostics if
712+ -- an exception strikes between modifyVar but before
713+ -- publishDiagnosticsNotification.
397− modifyVar diagnostics $ \old -> do
714+ newDiags <- modifyVar diagnostics $ \old -> do
398− let oldDiags = getFileDiagnostics fp old
399− let newDiagsStore = setStageDiagnostics fp (vfsVersion =<< modTime) (T.pack $ show k) current old
715+ let newDiagsStore = setStageDiagnostics fp (vfsVersion =<< modTime)
716+ (T.pack $ show k) (map snd currentShown) old
400 let newDiags = getFileDiagnostics fp newDiagsStore
717 let newDiags = getFileDiagnostics fp newDiagsStore
718+ _ <- evaluate newDiagsStore
719+ _ <- evaluate newDiags
401− pure (newDiagsStore, (newDiags, oldDiags))
720+ pure (newDiagsStore, newDiags)
402− when (newDiags /= oldDiags) $
721+ modifyVar_ hiddenDiagnostics $ \old -> do
722+ let newDiagsStore = setStageDiagnostics fp (vfsVersion =<< modTime)
723+ (T.pack $ show k) (map snd currentHidden) old
724+ let newDiags = getFileDiagnostics fp newDiagsStore
725+ _ <- evaluate newDiagsStore
726+ _ <- evaluate newDiags
727+ return newDiagsStore
728+ let uri = filePathToUri' fp
729+ let delay = if null newDiags then 0.1 else 0
730+ registerEvent debouncer delay uri $ do
731+ mask_ $ modifyVar_ publishedDiagnostics $ \published -> do
732+ let lastPublish = HMap.lookupDefault [] uri published
733+ when (lastPublish /= newDiags) $
403− sendEvent $ publishDiagnosticsNotification fp newDiags
734+ eventer $ publishDiagnosticsNotification (fromNormalizedUri uri) newDiags
404−
735+ pure $! HMap.insert uri newDiags published
736+
405−publishDiagnosticsNotification :: NormalizedFilePath -> [Diagnostic] -> LSP.FromServerMessage
737+publishDiagnosticsNotification :: Uri -> [Diagnostic] -> LSP.FromServerMessage
406−publishDiagnosticsNotification fp diags =
738+publishDiagnosticsNotification uri diags =
407 LSP.NotPublishDiagnostics $
739 LSP.NotPublishDiagnostics $
408 LSP.NotificationMessage "2.0" LSP.TextDocumentPublishDiagnostics $
740 LSP.NotificationMessage "2.0" LSP.TextDocumentPublishDiagnostics $
409− LSP.PublishDiagnosticsParams (fromNormalizedUri $ filePathToUri' fp) (List diags)
741+ LSP.PublishDiagnosticsParams uri (List diags)
410
742
743+newtype Priority = Priority Double
744+
411−setPriority :: (Enum a) => a -> Action ()
745+setPriority :: Priority -> Action ()
412−setPriority p =
746+setPriority (Priority p) = reschedule p
413− deprioritize (fromIntegral . negate $ fromEnum p)
414
747
415sendEvent :: LSP.FromServerMessage -> Action ()
748sendEvent :: LSP.FromServerMessage -> Action ()
416sendEvent e = do
749sendEvent e = do
@@ -420,25 +753,37 @@ sendEvent e = do
420ideLogger :: IdeState -> Logger
753ideLogger :: IdeState -> Logger
421ideLogger IdeState{shakeExtras=ShakeExtras{logger}} = logger
754ideLogger IdeState{shakeExtras=ShakeExtras{logger}} = logger
422
755
756+actionLogger :: Action Logger
757+actionLogger = do
758+ ShakeExtras{logger} <- getShakeExtras
759+ return logger
760+
423
761
424data GetModificationTime = GetModificationTime
762data GetModificationTime = GetModificationTime
425 deriving (Eq, Show, Generic)
763 deriving (Eq, Show, Generic)
426instance Hashable GetModificationTime
764instance Hashable GetModificationTime
427instance NFData GetModificationTime
765instance NFData GetModificationTime
766+instance Binary GetModificationTime
428
767
429-- | Get the modification time of a file.
768-- | Get the modification time of a file.
430type instance RuleResult GetModificationTime = FileVersion
769type instance RuleResult GetModificationTime = FileVersion
431
770
771+data FileVersion
772+ = VFSVersion !Int
432−data FileVersion = VFSVersion Int | ModificationTime UTCTime
773+ | ModificationTime
774+ !Int -- ^ Large unit (platform dependent, do not make assumptions)
775+ !Int -- ^ Small unit (platform dependent, do not make assumptions)
433 deriving (Show, Generic)
776 deriving (Show, Generic)
434
777
435instance NFData FileVersion
778instance NFData FileVersion
436
779
437vfsVersion :: FileVersion -> Maybe Int
780vfsVersion :: FileVersion -> Maybe Int
438vfsVersion (VFSVersion i) = Just i
781vfsVersion (VFSVersion i) = Just i
439−vfsVersion (ModificationTime _) = Nothing
782+vfsVersion ModificationTime{} = Nothing
440−
441
783
784+modificationTime :: FileVersion -> Maybe (Int, Int)
785+modificationTime VFSVersion{} = Nothing
786+modificationTime (ModificationTime large small) = Just (large, small)
442
787
443getDiagnosticsFromStore :: StoreItem -> [Diagnostic]
788getDiagnosticsFromStore :: StoreItem -> [Diagnostic]
444getDiagnosticsFromStore (StoreItem _ diags) = concatMap SL.fromSortedList $ Map.elems diags
789getDiagnosticsFromStore (StoreItem _ diags) = concatMap SL.fromSortedList $ Map.elems diags
@@ -446,14 +791,13 @@ getDiagnosticsFromStore (StoreItem _ diags) = concatMap SL.fromSortedList $ Map.
446
791
447-- | Sets the diagnostics for a file and compilation step
792-- | Sets the diagnostics for a file and compilation step
448-- if you want to clear the diagnostics call this with an empty list
793-- if you want to clear the diagnostics call this with an empty list
449−setStageDiagnostics ::
794+setStageDiagnostics
450− NormalizedFilePath ->
795+ :: NormalizedFilePath
451− Maybe Int ->
452− -- ^ the time that the file these diagnostics originate from was last edited
796+ -> TextDocumentVersion -- ^ the time that the file these diagnostics originate from was last edited
453− T.Text ->
797+ -> T.Text
454− [LSP.Diagnostic] ->
798+ -> [LSP.Diagnostic]
455− DiagnosticStore ->
799+ -> DiagnosticStore
456− DiagnosticStore
800+ -> DiagnosticStore
457setStageDiagnostics fp timeM stage diags ds =
801setStageDiagnostics fp timeM stage diags ds =
458 updateDiagnostics ds uri timeM diagsBySource
802 updateDiagnostics ds uri timeM diagsBySource
459 where
803 where
@@ -464,7 +808,7 @@ getAllDiagnostics ::
464 DiagnosticStore ->
808 DiagnosticStore ->
465 [FileDiagnostic]
809 [FileDiagnostic]
466getAllDiagnostics =
810getAllDiagnostics =
467− concatMap (\(k,v) -> map (fromUri k,) $ getDiagnosticsFromStore v) . Map.toList
811+ concatMap (\(k,v) -> map (fromUri k,ShowDiag,) $ getDiagnosticsFromStore v) . HMap.toList
468
812
469getFileDiagnostics ::
813getFileDiagnostics ::
470 NormalizedFilePath ->
814 NormalizedFilePath ->
@@ -472,11 +816,34 @@ getFileDiagnostics ::
472 [LSP.Diagnostic]
816 [LSP.Diagnostic]
473getFileDiagnostics fp ds =
817getFileDiagnostics fp ds =
474 maybe [] getDiagnosticsFromStore $
818 maybe [] getDiagnosticsFromStore $
475− Map.lookup (filePathToUri' fp) ds
819+ HMap.lookup (filePathToUri' fp) ds
476
820
477filterDiagnostics ::
821filterDiagnostics ::
478 (NormalizedFilePath -> Bool) ->
822 (NormalizedFilePath -> Bool) ->
479 DiagnosticStore ->
823 DiagnosticStore ->
480 DiagnosticStore
824 DiagnosticStore
481filterDiagnostics keep =
825filterDiagnostics keep =
482− Map.filterWithKey (\uri _ -> maybe True (keep . toNormalizedFilePath) $ uriToFilePath' $ fromNormalizedUri uri)
826+ HMap.filterWithKey (\uri _ -> maybe True (keep . toNormalizedFilePath') $ uriToFilePath' $ fromNormalizedUri uri)
827+
828+filterVersionMap
829+ :: HMap.HashMap NormalizedUri (Set.Set TextDocumentVersion)
830+ -> HMap.HashMap NormalizedUri (Map TextDocumentVersion a)
831+ -> HMap.HashMap NormalizedUri (Map TextDocumentVersion a)
832+filterVersionMap =
833+ HMap.intersectionWith $ \versionsToKeep versionMap -> Map.restrictKeys versionMap versionsToKeep
834+
835+updatePositionMapping :: IdeState -> VersionedTextDocumentIdentifier -> List TextDocumentContentChangeEvent -> IO ()
836+updatePositionMapping IdeState{shakeExtras = ShakeExtras{positionMapping}} VersionedTextDocumentIdentifier{..} (List changes) = do
837+ modifyVar_ positionMapping $ \allMappings -> do
838+ 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
src/Development/IDE/GHC/CPP.hs+46−3
@@ -1,12 +1,15 @@
1−-- Copyright (c) 2019 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
1+-- Copyright (c) 2019 The DAML Authors. All rights reserved.
2-- SPDX-License-Identifier: Apache-2.0
2-- SPDX-License-Identifier: Apache-2.0
3
3
4-- Copied from https://github.com/ghc/ghc/blob/master/compiler/main/DriverPipeline.hs on 14 May 2019
4-- Copied from https://github.com/ghc/ghc/blob/master/compiler/main/DriverPipeline.hs on 14 May 2019
5−-- Requested to be exposed at https://gitlab.haskell.org/ghc/ghc/merge_requests/944
5+-- Requested to be exposed at https://gitlab.haskell.org/ghc/ghc/merge_requests/944.
6+-- Update the above MR got merged to master on 31 May 2019. When it becomes avialable to ghc-lib, this file can be removed.
7+
6{- HLINT ignore -} -- since copied from upstream
8{- HLINT ignore -} -- since copied from upstream
7
9
8{-# LANGUAGE CPP, NamedFieldPuns, NondecreasingIndentation, BangPatterns, MultiWayIf #-}
10{-# LANGUAGE CPP, NamedFieldPuns, NondecreasingIndentation, BangPatterns, MultiWayIf #-}
9{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}
11{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}
12+#include "ghc-api-version.h"
10
13
11-----------------------------------------------------------------------------
14-----------------------------------------------------------------------------
12--
15--
@@ -16,14 +19,25 @@
16--
19--
17-----------------------------------------------------------------------------
20-----------------------------------------------------------------------------
18
21
19−module Development.IDE.GHC.CPP(doCpp) where
22+module Development.IDE.GHC.CPP(doCpp, addOptP)
23+where
20
24
25+import Development.IDE.GHC.Compat
21import Packages
26import Packages
22import SysTools
27import SysTools
23import Module
28import Module
24import DynFlags
29import DynFlags
25import Panic
30import Panic
26import FileCleanup
31import FileCleanup
32+#if MIN_GHC_API_VERSION(8,8,2)
33+import LlvmCodeGen (llvmVersionList)
34+#elif MIN_GHC_API_VERSION(8,8,0)
35+import LlvmCodeGen (LlvmVersion (..))
36+#endif
37+#if MIN_GHC_API_VERSION (8,10,0)
38+import Fingerprint
39+import ToolSettings
40+#endif
27
41
28import System.Directory
42import System.Directory
29import System.FilePath
43import System.FilePath
@@ -50,7 +64,11 @@ doCpp dflags raw input_fn output_fn = do
50 let verbFlags = getVerbFlags dflags
64 let verbFlags = getVerbFlags dflags
51
65
52 let cpp_prog args | raw = SysTools.runCpp dflags args
66 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
53 | otherwise = SysTools.runCc
70 | otherwise = SysTools.runCc
71+#endif
54 dflags (SysTools.Option "-E" : args)
72 dflags (SysTools.Option "-E" : args)
55
73
56 let target_defs =
74 let target_defs =
@@ -130,7 +148,16 @@ getBackendDefs :: DynFlags -> IO [String]
130getBackendDefs dflags | hscTarget dflags == HscLlvm = do
148getBackendDefs dflags | hscTarget dflags == HscLlvm = do
131 llvmVer <- figureLlvmVersion dflags
149 llvmVer <- figureLlvmVersion dflags
132 return $ case llvmVer of
150 return $ case llvmVer of
151+#if MIN_GHC_API_VERSION(8,8,2)
152+ Just v
153+ | [m] <- llvmVersionList v -> [ "-D__GLASGOW_HASKELL_LLVM__=" ++ format (m, 0) ]
154+ | m:n:_ <- llvmVersionList v -> [ "-D__GLASGOW_HASKELL_LLVM__=" ++ format (m, n) ]
155+#elif MIN_GHC_API_VERSION(8,8,0)
156+ Just (LlvmVersion n) -> [ "-D__GLASGOW_HASKELL_LLVM__=" ++ format (n,0) ]
157+ Just (LlvmVersionOld m n) -> [ "-D__GLASGOW_HASKELL_LLVM__=" ++ format (m,n) ]
158+#else
133 Just n -> [ "-D__GLASGOW_HASKELL_LLVM__=" ++ format n ]
159 Just n -> [ "-D__GLASGOW_HASKELL_LLVM__=" ++ format n ]
160+#endif
134 _ -> []
161 _ -> []
135 where
162 where
136 format (major, minor)
163 format (major, minor)
@@ -140,6 +167,22 @@ getBackendDefs dflags | hscTarget dflags == HscLlvm = do
140getBackendDefs _ =
167getBackendDefs _ =
141 return []
168 return []
142
169
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+
143-- ---------------------------------------------------------------------------
186-- ---------------------------------------------------------------------------
144-- Macros (cribbed from Cabal)
187-- Macros (cribbed from Cabal)
145
188
src/Development/IDE/GHC/Compat.hs+275−14
@@ -1,32 +1,100 @@
1−-- Copyright (c) 2019 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
1+-- Copyright (c) 2019 The DAML Authors. All rights reserved.
2-- SPDX-License-Identifier: Apache-2.0
2-- SPDX-License-Identifier: Apache-2.0
3
3
4{-# LANGUAGE CPP #-}
4{-# LANGUAGE CPP #-}
5+{-# LANGUAGE PatternSynonyms #-}
6+#include "ghc-api-version.h"
5
7
6-- | Attempt at hiding the GHC version differences we can.
8-- | Attempt at hiding the GHC version differences we can.
7module Development.IDE.GHC.Compat(
9module Development.IDE.GHC.Compat(
10+ getHeaderImports,
8 HieFileResult(..),
11 HieFileResult(..),
9− HieFile(..),
12+ HieFile,
13+ hieExportNames,
14+ hie_module,
10 mkHieFile,
15 mkHieFile,
11 writeHieFile,
16 writeHieFile,
12 readHieFile,
17 readHieFile,
18+ supportsHieFiles,
19+ setDefaultHieDir,
20+ dontWriteHieFiles,
21+#if !MIN_GHC_API_VERSION(8,8,0)
22+ ml_hie_file,
23+#endif
13− hPutStringBuffer
24+ hPutStringBuffer,
25+ includePathsGlobal,
26+ includePathsQuote,
27+ addIncludePathsQuote,
28+ getModuleHash,
29+ getPackageName,
30+ pattern DerivD,
31+ pattern ForD,
32+ pattern InstD,
33+ pattern TyClD,
34+ pattern ValD,
35+ pattern ClassOpSig,
36+ pattern IEThingAll,
37+ pattern IEThingWith,
38+ GHC.ModLocation,
39+ Module.addBootSuffix,
40+ pattern ModLocation,
41+ getConArgs,
42+
43+ module GHC
14 ) where
44 ) where
15
45
16import StringBuffer
46import StringBuffer
47+import DynFlags
48+import FieldLabel
49+import Fingerprint (Fingerprint)
50+import qualified Module
51+import Packages
52+
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
58+#endif
59+ )
60+import qualified HeaderInfo as Hdr
61+import Avail
62+import ErrUtils (ErrorMessages)
63+import FastString (FastString)
17
64
18−#ifndef GHC_STABLE
65+#if MIN_GHC_API_VERSION(8,10,0)
19−import HieAst
66+import HscTypes (mi_mod_hash)
67+#endif
68+
69+#if MIN_GHC_API_VERSION(8,8,0)
70+import Control.Applicative ((<|>))
71+import Development.IDE.GHC.HieAst (mkHieFile)
20import HieBin
72import HieBin
21import HieTypes
73import HieTypes
74+
75+supportsHieFiles :: Bool
76+supportsHieFiles = True
77+
78+hieExportNames :: HieFile -> [(SrcSpan, Name)]
79+hieExportNames = nameListFromAvails . hie_exports
80+
22#else
81#else
82+
83+#if MIN_GHC_API_VERSION(8,6,0)
23−import GHC
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)
24−import GhcPlugins
92+import GhcPlugins hiding (ModLocation)
25import NameCache
93import NameCache
26−import Avail
27import TcRnTypes
94import TcRnTypes
28import System.IO
95import System.IO
29import Foreign.ForeignPtr
96import Foreign.ForeignPtr
97+import MkIface
30
98
31
99
32hPutStringBuffer :: Handle -> StringBuffer -> IO ()
100hPutStringBuffer :: Handle -> StringBuffer -> IO ()
@@ -34,15 +102,208 @@ hPutStringBuffer hdl (StringBuffer buf len cur)
34 = withForeignPtr (plusForeignPtr buf cur) $ \ptr ->
102 = withForeignPtr (plusForeignPtr buf cur) $ \ptr ->
35 hPutBuf hdl ptr len
103 hPutBuf hdl ptr len
36
104
105+#endif
106+
107+#if !MIN_GHC_API_VERSION(8,6,0)
108+includePathsGlobal, includePathsQuote :: [String] -> [String]
109+includePathsGlobal = id
110+includePathsQuote = const []
111+#endif
112+
113+
114+addIncludePathsQuote :: FilePath -> DynFlags -> DynFlags
115+#if MIN_GHC_API_VERSION(8,6,0)
116+addIncludePathsQuote path x = x{includePaths = f $ includePaths x}
117+ where f i = i{includePathsQuote = path : includePathsQuote i}
118+#else
119+addIncludePathsQuote path x = x{includePaths = path : includePaths x}
120+#endif
121+
122+pattern DerivD :: DerivDecl p -> HsDecl p
123+pattern DerivD x <-
124+#if MIN_GHC_API_VERSION(8,6,0)
125+ GHC.DerivD _ x
126+#else
127+ GHC.DerivD x
128+#endif
129+
130+pattern ForD :: ForeignDecl p -> HsDecl p
131+pattern ForD x <-
132+#if MIN_GHC_API_VERSION(8,6,0)
133+ GHC.ForD _ x
134+#else
135+ GHC.ForD x
136+#endif
137+
138+pattern ValD :: HsBind p -> HsDecl p
139+pattern ValD x <-
140+#if MIN_GHC_API_VERSION(8,6,0)
141+ GHC.ValD _ x
142+#else
143+ GHC.ValD x
144+#endif
145+
146+pattern InstD :: InstDecl p -> HsDecl p
147+pattern InstD x <-
148+#if MIN_GHC_API_VERSION(8,6,0)
149+ GHC.InstD _ x
150+#else
151+ GHC.InstD x
152+#endif
153+
154+pattern TyClD :: TyClDecl p -> HsDecl p
155+pattern TyClD x <-
156+#if MIN_GHC_API_VERSION(8,6,0)
157+ GHC.TyClD _ x
158+#else
159+ GHC.TyClD x
160+#endif
161+
162+pattern ClassOpSig :: Bool -> [Located (IdP pass)] -> LHsSigType pass -> Sig pass
163+pattern ClassOpSig a b c <-
164+#if MIN_GHC_API_VERSION(8,6,0)
165+ GHC.ClassOpSig _ a b c
166+#else
167+ GHC.ClassOpSig a b c
168+#endif
169+
170+pattern IEThingWith :: LIEWrappedName (IdP pass) -> IEWildcard -> [LIEWrappedName (IdP pass)] -> [Located (FieldLbl (IdP pass))] -> IE pass
171+pattern IEThingWith a b c d <-
172+#if MIN_GHC_API_VERSION(8,6,0)
173+ GHC.IEThingWith _ a b c d
174+#else
175+ 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+
37−mkHieFile :: ModSummary -> TcGblEnv -> RenamedSource -> Hsc HieFile
217+mkHieFile :: ModSummary -> TcGblEnv -> RenamedSource -> ByteString -> Hsc HieFile
38−mkHieFile _ _ _ = return (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 }
39
241
40writeHieFile :: FilePath -> HieFile -> IO ()
242writeHieFile :: 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+
41writeHieFile _ _ = return ()
265writeHieFile _ _ = return ()
42
266
43−readHieFile :: NameCache -> FilePath -> IO (HieFileResult, ())
267+readHieFile _ _ = return undefined
44−readHieFile _ _ = return (HieFileResult (HieFile () []), ())
268+
269+#endif
45
270
46−data HieFile = HieFile {hie_module :: (), hie_exports :: [AvailInfo]}
47−data HieFileResult = HieFileResult { hie_file_result :: HieFile }
48#endif
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))
src/Development/IDE/GHC/Error.hs+67−18
@@ -1,6 +1,5 @@
1−-- Copyright (c) 2019 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
1+-- Copyright (c) 2019 The DAML Authors. All rights reserved.
2-- SPDX-License-Identifier: Apache-2.0
2-- SPDX-License-Identifier: Apache-2.0
3−{-# LANGUAGE OverloadedStrings #-}
4module Development.IDE.GHC.Error
3module Development.IDE.GHC.Error
5 (
4 (
6 -- * Producing Diagnostic values
5 -- * Producing Diagnostic values
@@ -9,12 +8,20 @@ module Development.IDE.GHC.Error
9 , diagFromString
8 , diagFromString
10 , diagFromStrings
9 , diagFromStrings
11 , diagFromGhcException
10 , diagFromGhcException
11+ , catchSrcErrors
12+ , mergeDiagnostics
12
13
13 -- * utilities working with spans
14 -- * utilities working with spans
14 , srcSpanToLocation
15 , srcSpanToLocation
16+ , srcSpanToRange
15 , srcSpanToFilename
17 , srcSpanToFilename
16 , zeroSpan
18 , zeroSpan
17 , realSpan
19 , realSpan
20+ , isInsideSrcSpan
21+ , noSpan
22+
23+ -- * utilities working with severities
24+ , toDSeverity
18 ) where
25 ) where
19
26
20import Development.IDE.Types.Diagnostics as D
27import Development.IDE.Types.Diagnostics as D
@@ -24,33 +31,58 @@ import Development.IDE.GHC.Orphans()
24import qualified FastString as FS
31import qualified FastString as FS
25import GHC
32import GHC
26import Bag
33import Bag
34+import DynFlags
35+import HscTypes
36+import Panic
27import ErrUtils
37import ErrUtils
28import SrcLoc
38import SrcLoc
29import qualified Outputable as Out
39import qualified Outputable as Out
40+import Exception (ExceptionMonad)
30
41
31
42
32
43
33−diagFromText :: D.DiagnosticSeverity -> SrcSpan -> T.Text -> FileDiagnostic
44+diagFromText :: T.Text -> D.DiagnosticSeverity -> SrcSpan -> T.Text -> FileDiagnostic
34−diagFromText sev loc msg = (toNormalizedFilePath $ srcSpanToFilename loc,)
45+diagFromText diagSource sev loc msg = (toNormalizedFilePath' $ srcSpanToFilename loc,ShowDiag,)
35 Diagnostic
46 Diagnostic
36 { _range = srcSpanToRange loc
47 { _range = srcSpanToRange loc
37 , _severity = Just sev
48 , _severity = Just sev
38− , _source = Just "compiler" -- should really be 'daml' or 'haskell', but not shown in the IDE so who cares
49+ , _source = Just diagSource -- not shown in the IDE, but useful for ghcide developers
39 , _message = msg
50 , _message = msg
40 , _code = Nothing
51 , _code = Nothing
41 , _relatedInformation = Nothing
52 , _relatedInformation = Nothing
53+ , _tags = Nothing
42 }
54 }
43
55
44-- | Produce a GHC-style error from a source span and a message.
56-- | Produce a GHC-style error from a source span and a message.
45−diagFromErrMsg :: DynFlags -> ErrMsg -> [FileDiagnostic]
57+diagFromErrMsg :: T.Text -> DynFlags -> ErrMsg -> [FileDiagnostic]
46−diagFromErrMsg dflags e =
58+diagFromErrMsg diagSource dflags e =
47− [ diagFromText sev (errMsgSpan e) $ T.pack $ Out.showSDoc dflags $ ErrUtils.pprLocErrMsg e
59+ [ diagFromText diagSource sev (errMsgSpan e) $ T.pack $ Out.showSDoc dflags $
60+ ErrUtils.formatErrDoc dflags $ ErrUtils.errMsgDoc e
48 | Just sev <- [toDSeverity $ errMsgSeverity e]]
61 | Just sev <- [toDSeverity $ errMsgSeverity e]]
49
62
50
63
51−diagFromErrMsgs :: DynFlags -> Bag ErrMsg -> [FileDiagnostic]
64+diagFromErrMsgs :: T.Text -> DynFlags -> Bag ErrMsg -> [FileDiagnostic]
52−diagFromErrMsgs dflags = concatMap (diagFromErrMsg dflags) . bagToList
65+diagFromErrMsgs diagSource dflags = concatMap (diagFromErrMsg diagSource dflags) . bagToList
53−
66+
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
54
86
55-- | Convert a GHC SrcSpan to a DAML compiler Range
87-- | Convert a GHC SrcSpan to a DAML compiler Range
56srcSpanToRange :: SrcSpan -> Range
88srcSpanToRange :: SrcSpan -> Range
@@ -71,7 +103,11 @@ srcSpanToFilename (RealSrcSpan real) = FS.unpackFS $ srcSpanFile real
71srcSpanToLocation :: SrcSpan -> Location
103srcSpanToLocation :: SrcSpan -> Location
72srcSpanToLocation src =
104srcSpanToLocation src =
73 -- important that the URI's we produce have been properly normalized, otherwise they point at weird places in VS Code
105 -- important that the URI's we produce have been properly normalized, otherwise they point at weird places in VS Code
74− Location (fromNormalizedUri $ filePathToUri' $ toNormalizedFilePath $ srcSpanToFilename src) (srcSpanToRange src)
106+ Location (fromNormalizedUri $ filePathToUri' $ toNormalizedFilePath' $ srcSpanToFilename src) (srcSpanToRange src)
107+
108+isInsideSrcSpan :: Position -> SrcSpan -> Bool
109+p `isInsideSrcSpan` r = sp <= p && p <= ep
110+ where Range sp ep = srcSpanToRange r
75
111
76-- | Convert a GHC severity to a DAML compiler Severity. Severities below
112-- | Convert a GHC severity to a DAML compiler Severity. Severities below
77-- "Warning" level are dropped (returning Nothing).
113-- "Warning" level are dropped (returning Nothing).
@@ -87,12 +123,12 @@ toDSeverity SevFatal = Just DsError
87
123
88-- | Produce a bag of GHC-style errors (@ErrorMessages@) from the given
124-- | Produce a bag of GHC-style errors (@ErrorMessages@) from the given
89-- (optional) locations and message strings.
125-- (optional) locations and message strings.
90−diagFromStrings :: [(SrcSpan, String)] -> [FileDiagnostic]
126+diagFromStrings :: T.Text -> D.DiagnosticSeverity -> [(SrcSpan, String)] -> [FileDiagnostic]
91−diagFromStrings = concatMap (uncurry diagFromString)
127+diagFromStrings diagSource sev = concatMap (uncurry (diagFromString diagSource sev))
92
128
93-- | Produce a GHC-style error from a source span and a message.
129-- | Produce a GHC-style error from a source span and a message.
94−diagFromString :: SrcSpan -> String -> [FileDiagnostic]
130+diagFromString :: T.Text -> D.DiagnosticSeverity -> SrcSpan -> String -> [FileDiagnostic]
95−diagFromString sp x = [diagFromText DsError sp $ T.pack x]
131+diagFromString diagSource sev sp x = [diagFromText diagSource sev sp $ T.pack x]
96
132
97
133
98-- | Produces an "unhelpful" source span with the given string.
134-- | Produces an "unhelpful" source span with the given string.
@@ -112,8 +148,21 @@ realSpan = \case
112 UnhelpfulSpan _ -> Nothing
148 UnhelpfulSpan _ -> Nothing
113
149
114
150
151+-- | Run something in a Ghc monad and catch the errors (SourceErrors and
152+-- compiler-internal exceptions like Panic or InstallationError).
153+catchSrcErrors :: (HasDynFlags m, ExceptionMonad m) => T.Text -> m a -> m (Either [FileDiagnostic] a)
154+catchSrcErrors fromWhere ghcM = do
155+ dflags <- getDynFlags
156+ handleGhcException (ghcExceptionToDiagnostics dflags) $
157+ handleSourceError (sourceErrorToDiagnostics dflags) $
158+ Right <$> ghcM
159+ where
160+ ghcExceptionToDiagnostics dflags = return . Left . diagFromGhcException fromWhere dflags
161+ sourceErrorToDiagnostics dflags = return . Left . diagFromErrMsgs fromWhere dflags . srcErrorMessages
162+
163+
115−diagFromGhcException :: DynFlags -> GhcException -> [FileDiagnostic]
164+diagFromGhcException :: T.Text -> DynFlags -> GhcException -> [FileDiagnostic]
116−diagFromGhcException dflags exc = diagFromString (noSpan "<Internal>") (showGHCE dflags exc)
165+diagFromGhcException diagSource dflags exc = diagFromString diagSource DsError (noSpan "<Internal>") (showGHCE dflags exc)
117
166
118showGHCE :: DynFlags -> GhcException -> String
167showGHCE :: DynFlags -> GhcException -> String
119showGHCE dflags exc = case exc of
168showGHCE dflags exc = case exc of
src/Development/IDE/GHC/Orphans.hs+16−10
@@ -1,8 +1,10 @@
1−-- Copyright (c) 2019 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
1+-- Copyright (c) 2019 The DAML Authors. All rights reserved.
2-- SPDX-License-Identifier: Apache-2.0
2-- SPDX-License-Identifier: Apache-2.0
3
3
4+{-# LANGUAGE CPP #-}
4{-# LANGUAGE FlexibleInstances #-}
5{-# LANGUAGE FlexibleInstances #-}
5{-# OPTIONS_GHC -Wno-orphans #-}
6{-# OPTIONS_GHC -Wno-orphans #-}
7+#include "ghc-api-version.h"
6
8
7-- | Orphan instances for GHC.
9-- | Orphan instances for GHC.
8-- Note that the 'NFData' instances may not be law abiding.
10-- Note that the 'NFData' instances may not be law abiding.
@@ -20,12 +22,18 @@ import Development.IDE.GHC.Util
20-- Orphan instances for types from the GHC API.
22-- Orphan instances for types from the GHC API.
21instance Show CoreModule where show = prettyPrint
23instance Show CoreModule where show = prettyPrint
22instance NFData CoreModule where rnf = rwhnf
24instance NFData CoreModule where rnf = rwhnf
23−
25+instance Show CgGuts where show = prettyPrint . cg_module
26+instance NFData CgGuts where rnf = rwhnf
27+instance Show ModDetails where show = const "<moddetails>"
28+instance NFData ModDetails where rnf = rwhnf
29+instance NFData SafeHaskellMode where rnf = rwhnf
30+instance Show Linkable where show = prettyPrint
31+instance NFData Linkable where rnf = rwhnf
24
32
25instance Show InstalledUnitId where
33instance Show InstalledUnitId where
26 show = installedUnitIdString
34 show = installedUnitIdString
27
35
28−instance NFData InstalledUnitId where rnf = rwhnf
36+instance NFData InstalledUnitId where rnf = rwhnf . installedUnitIdFS
29
37
30instance NFData SB.StringBuffer where rnf = rwhnf
38instance NFData SB.StringBuffer where rnf = rwhnf
31
39
@@ -33,10 +41,9 @@ instance Show Module where
33 show = moduleNameString . moduleName
41 show = moduleNameString . moduleName
34
42
35instance Show (GenLocated SrcSpan ModuleName) where show = prettyPrint
43instance Show (GenLocated SrcSpan ModuleName) where show = prettyPrint
36−instance Show Name where show = prettyPrint
37
44
38−instance NFData (GenLocated SrcSpan ModuleName) where
45+instance (NFData l, NFData e) => NFData (GenLocated l e) where
39− rnf = rwhnf
46+ rnf (L l e) = rnf l `seq` rnf e
40
47
41instance Show ModSummary where
48instance Show ModSummary where
42 show = show . ms_mod
49 show = show . ms_mod
@@ -47,11 +54,10 @@ instance Show ParsedModule where
47instance NFData ModSummary where
54instance NFData ModSummary where
48 rnf = rwhnf
55 rnf = rwhnf
49
56
50−instance Show HscEnv where
57+#if !MIN_GHC_API_VERSION(8,10,0)
51− show _ = "HscEnv"
52−
53−instance NFData HscEnv where
58+instance NFData FastString where
54 rnf = rwhnf
59 rnf = rwhnf
60+#endif
55
61
56instance NFData ParsedModule where
62instance NFData ParsedModule where
57 rnf = rwhnf
63 rnf = rwhnf
src/Development/IDE/GHC/Util.hs+267−44
@@ -1,38 +1,83 @@
1−-- Copyright (c) 2019 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
1+-- Copyright (c) 2019 The DAML Authors. All rights reserved.
2-- SPDX-License-Identifier: Apache-2.0
2-- SPDX-License-Identifier: Apache-2.0
3
3
4−{-# OPTIONS_GHC -Wno-missing-fields #-} -- to enable prettyPrint
4+-- | General utility functions, mostly focused around GHC operations.
5−
6−-- | GHC utility functions. Importantly, code using our GHC should never:
7−--
8−-- * Call runGhc, use runGhcFast instead. It's faster and doesn't require config we don't have.
9−--
10−-- * Call setSessionDynFlags, use modifyDynFlags instead. It's faster and avoids loading packages.
11module Development.IDE.GHC.Util(
5module Development.IDE.GHC.Util(
12− lookupPackageConfig,
6+ -- * HcsEnv and environment
7+ HscEnvEq(GhcVersionMismatch, compileTime, runTime), hscEnv, newHscEnvEq,
13 modifyDynFlags,
8 modifyDynFlags,
14− fakeDynFlags,
9+ evalGhcEnv,
15− prettyPrint,
16 runGhcEnv,
10 runGhcEnv,
11+ deps,
12+ -- * GHC wrappers
13+ prettyPrint,
14+ printRdrName,
15+ printName,
16+ ParseResult(..), runParser,
17+ lookupPackageConfig,
17− textToStringBuffer
18+ textToStringBuffer,
19+ stringBufferToByteString,
20+ moduleImportPath,
21+ cgGutsToCoreModule,
22+ fingerprintToBS,
23+ fingerprintFromStringBuffer,
24+ -- * General utilities
25+ readFileUtf8,
26+ hDuplicateTo',
27+ setDefaultHieDir,
28+ dontWriteHieFiles
18 ) where
29 ) where
19
30
20−import Config
31+import Control.Concurrent
32+import Data.List.Extra
33+import Data.ByteString.Internal (ByteString(..))
34+import Data.Maybe
35+import Data.Typeable
36+import qualified Data.ByteString.Internal as BS
21import Fingerprint
37import Fingerprint
22−import GHC
23import GhcMonad
38import GhcMonad
24−import GhcPlugins
25−import Data.IORef
26import Control.Exception
39import Control.Exception
40+import Data.IORef
41+import Data.Version (showVersion, Version)
27import FileCleanup
42import FileCleanup
28−import Platform
43+import Foreign.Ptr
44+import Foreign.ForeignPtr
45+import Foreign.Storable
46+import GHC.IO.BufferedIO (BufferedIO)
47+import GHC.IO.Device as IODevice
48+import GHC.IO.Encoding
49+import GHC.IO.Exception
50+import GHC.IO.Handle.Types
51+import GHC.IO.Handle.Internals
52+import Data.Unique
53+import Development.Shake.Classes
54+import qualified Data.Text as T
55+import qualified Data.Text.Encoding as T
29−import qualified Data.Text as T
56+import qualified Data.Text.Encoding.Error as T
57+import qualified Data.ByteString as BS
58+import Lexer
30import StringBuffer
59import StringBuffer
60+import 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)
71+
72+import Development.IDE.GHC.Compat as GHC
73+import Development.IDE.Types.Location
31
74
32
75
33----------------------------------------------------------------------
76----------------------------------------------------------------------
34-- GHC setup
77-- GHC setup
35
78
79+-- | Used to modify dyn flags in preference to calling 'setSessionDynFlags',
80+-- since that function also reloads packages (which is very slow).
36modifyDynFlags :: GhcMonad m => (DynFlags -> DynFlags) -> m ()
81modifyDynFlags :: GhcMonad m => (DynFlags -> DynFlags) -> m ()
37modifyDynFlags f = do
82modifyDynFlags f = do
38 newFlags <- f <$> getSessionDynFlags
83 newFlags <- f <$> getSessionDynFlags
@@ -41,6 +86,7 @@ modifyDynFlags f = do
41 modifySession $ \h ->
86 modifySession $ \h ->
42 h { hsc_dflags = newFlags, hsc_IC = (hsc_IC h) {ic_dflags = newFlags} }
87 h { hsc_dflags = newFlags, hsc_IC = (hsc_IC h) {ic_dflags = newFlags} }
43
88
89+-- | Given a 'UnitId' try and find the associated 'PackageConfig' in the environment.
44lookupPackageConfig :: UnitId -> HscEnv -> Maybe PackageConfig
90lookupPackageConfig :: UnitId -> HscEnv -> Maybe PackageConfig
45lookupPackageConfig unitId env =
91lookupPackageConfig unitId env =
46 lookupPackage' False pkgConfigMap unitId
92 lookupPackage' False pkgConfigMap unitId
@@ -51,42 +97,219 @@ lookupPackageConfig unitId env =
51 getPackageConfigMap $ hsc_dflags env
97 getPackageConfigMap $ hsc_dflags env
52
98
53
99
54−-- would be nice to do this more efficiently...
100+-- | Convert from the @text@ package to the @GHC@ 'StringBuffer'.
101+-- Currently implemented somewhat inefficiently (if it ever comes up in a profile).
55textToStringBuffer :: T.Text -> StringBuffer
102textToStringBuffer :: T.Text -> StringBuffer
56textToStringBuffer = stringToStringBuffer . T.unpack
103textToStringBuffer = stringToStringBuffer . T.unpack
57
104
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
58
112
113+stringBufferToByteString :: StringBuffer -> ByteString
114+stringBufferToByteString StringBuffer{..} = PS buf cur len
115+
116+-- | Pretty print a GHC value using 'unsafeGlobalDynFlags '.
59prettyPrint :: Outputable a => a -> String
117prettyPrint :: Outputable a => a -> String
60−prettyPrint = showSDoc fakeDynFlags . ppr
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
125+
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
61
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'.
62−runGhcEnv :: HscEnv -> Ghc a -> IO a
137+runGhcEnv :: HscEnv -> Ghc a -> IO (HscEnv, a)
63runGhcEnv env act = do
138runGhcEnv env act = do
64 filesToClean <- newIORef emptyFilesToClean
139 filesToClean <- newIORef emptyFilesToClean
65 dirsToClean <- newIORef mempty
140 dirsToClean <- newIORef mempty
66− let dflags = (hsc_dflags env){filesToClean=filesToClean, dirsToClean=dirsToClean}
141+ let dflags = (hsc_dflags env){filesToClean=filesToClean, dirsToClean=dirsToClean, useUnicode=True}
67 ref <- newIORef env{hsc_dflags=dflags}
142 ref <- newIORef env{hsc_dflags=dflags}
68− unGhc act (Session ref) `finally` do
143+ res <- unGhc act (Session ref) `finally` do
69 cleanTempFiles dflags
144 cleanTempFiles dflags
70 cleanTempDirs dflags
145 cleanTempDirs dflags
146+ (,res) <$> readIORef ref
71
147
72−-- Fake DynFlags which are mostly undefined, but define enough to do a
148+-- | Given a module location, and its parse tree, figure out what is the include directory implied by it.
73−-- little bit.
149+-- For example, given the file @\/usr\/\Test\/Foo\/Bar.hs@ with the module name @Foo.Bar@ the directory
74−fakeDynFlags :: DynFlags
150+-- @\/usr\/Test@ should be on the include path to find sibling modules.
75−fakeDynFlags = defaultDynFlags settings ([], [])
151+moduleImportPath :: NormalizedFilePath -> GHC.ModuleName -> Maybe FilePath
76− where
152+-- The call to takeDirectory is required since DAML does not require that
77− settings = Settings
153+-- the file name matches the module name in the last component.
78− { sTargetPlatform = platform
154+-- Once that has changed we can get rid of this.
79− , sPlatformConstants = platformConstants
155+moduleImportPath (takeDirectory . fromNormalizedFilePath -> pathDir) mn
80− , sProgramName = "ghc"
156+ -- This happens for single-component modules since takeDirectory "A" == "."
81− , sProjectVersion = cProjectVersion
157+ | modDir == "." = Just pathDir
82− , sOpt_P_fingerprint = fingerprint0
158+ | otherwise = dropTrailingPathSeparator <$> stripSuffix modDir pathDir
83− }
159+ where
84− platform = Platform
160+ -- A for module A.B
85− { platformWordSize=8
161+ modDir =
86− , platformOS=OSUnknown
162+ takeDirectory $
87− , platformUnregisterised=True
163+ fromNormalizedFilePath $ toNormalizedFilePath' $
88− }
164+ moduleNameSlashes mn
89− platformConstants = PlatformConstants
165+
90− { pc_DYNAMIC_BY_DEFAULT=False
166+-- | An 'HscEnv' with equality. Two values are considered equal
91− , pc_WORD_SIZE=8
167+-- if they are created with the same call to 'newHscEnvEq'.
92− }
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+ }
176+
177+-- | Unwrap an 'HsEnvEq'.
178+hscEnv :: 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{} = []
195+
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
199+
200+instance Show HscEnvEq where
201+ show (HscEnvEq a _ _) = "HscEnvEq " ++ show (hashUnique a)
202+ show GhcVersionMismatch{..} = "GhcVersionMismatch " <> show (compileTime, runTime)
203+
204+instance Eq HscEnvEq where
205+ HscEnvEq a _ _ == HscEnvEq b _ _ = a == b
206+ GhcVersionMismatch a b == GhcVersionMismatch c d = a == c && b == d
207+ _ == _ = False
208+
209+instance NFData HscEnvEq where
210+ rnf (HscEnvEq a b c) = rnf (hashUnique a) `seq` b `seq` c `seq` ()
211+ rnf GhcVersionMismatch{} = rnf runTime
212+
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.
224+readFileUtf8 :: FilePath -> IO T.Text
225+readFileUtf8 f = T.decodeUtf8With T.lenientDecode <$> BS.readFile f
226+
227+-- | Convert from a 'CgGuts' to a 'CoreModule'.
228+cgGutsToCoreModule :: SafeHaskellMode -> CgGuts -> ModDetails -> CoreModule
229+cgGutsToCoreModule safeMode guts modDetails = CoreModule
230+ (cg_module guts)
231+ (md_types modDetails)
232+ (cg_binds guts)
233+ safeMode
234+
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.
251+hDuplicateTo' :: Handle -> Handle -> IO ()
252+hDuplicateTo' h1@(FileHandle path m1) h2@(FileHandle _ m2) = do
253+ withHandle__' "hDuplicateTo" h2 m2 $ \h2_ -> do
254+ -- The implementation in base has this call to hClose_help.
255+ -- _ <- hClose_help h2_
256+ -- hClose_help does two things:
257+ -- 1. It flushes the buffer, we replicate this here
258+ _ <- flushWriteBuffer h2_ `catch` \(_ :: IOException) -> pure ()
259+ -- 2. It closes the handle. This is redundant since dup2 takes care of that
260+ -- but even worse it is actively harmful! Once the handle has been closed
261+ -- another thread is free to reallocate it. This leads to dup2 failing with EBUSY
262+ -- if it happens just in the right moment.
263+ withHandle_' "hDuplicateTo" h1 m1 $ \h1_ -> do
264+ dupHandleTo path h1 Nothing h2_ h1_ (Just handleFinalizer)
265+hDuplicateTo' h1@(DuplexHandle path r1 w1) h2@(DuplexHandle _ r2 w2) = do
266+ withHandle__' "hDuplicateTo" h2 w2 $ \w2_ -> do
267+ _ <- hClose_help w2_
268+ withHandle_' "hDuplicateTo" h1 w1 $ \w1_ -> do
269+ dupHandleTo path h1 Nothing w2_ w1_ (Just handleFinalizer)
270+ withHandle__' "hDuplicateTo" h2 r2 $ \r2_ -> do
271+ _ <- hClose_help r2_
272+ withHandle_' "hDuplicateTo" h1 r1 $ \r1_ -> do
273+ dupHandleTo path h1 (Just w1) r2_ r1_ Nothing
274+hDuplicateTo' h1 _ =
275+ ioe_dupHandlesNotCompatible h1
276+
277+-- | This is copied unmodified from GHC since it is not exposed.
278+dupHandleTo :: FilePath
279+ -> Handle
280+ -> Maybe (MVar Handle__)
281+ -> Handle__
282+ -> Handle__
283+ -> Maybe HandleFinalizer
284+ -> IO Handle__
285+dupHandleTo filepath h other_side
286+ _hto_@Handle__{haDevice=devTo}
287+ h_@Handle__{haDevice=dev} mb_finalizer = do
288+ flushBuffer h_
289+ case cast devTo of
290+ Nothing -> ioe_dupHandlesNotCompatible h
291+ Just dev' -> do
292+ _ <- IODevice.dup2 dev dev'
293+ FileHandle _ m <- dupHandle_ dev' filepath other_side h_ mb_finalizer
294+ takeMVar m
295+
296+-- | This is copied unmodified from GHC since it is not exposed.
297+-- Note the beautiful inline comment!
298+dupHandle_ :: (IODevice dev, BufferedIO dev, Typeable dev) => dev
299+ -> FilePath
300+ -> Maybe (MVar Handle__)
301+ -> Handle__
302+ -> Maybe HandleFinalizer
303+ -> IO Handle
304+dupHandle_ new_dev filepath other_side _h_@Handle__{..} mb_finalizer = do
305+ -- XXX wrong!
306+ mb_codec <- if isJust haEncoder then fmap Just getLocaleEncoding else return Nothing
307+ mkHandle new_dev filepath haType True{-buffered-} mb_codec
308+ NewlineMode { inputNL = haInputNL, outputNL = haOutputNL }
309+ mb_finalizer other_side
310+
311+-- | This is copied unmodified from GHC since it is not exposed.
312+ioe_dupHandlesNotCompatible :: Handle -> IO a
313+ioe_dupHandlesNotCompatible h =
314+ ioException (IOError (Just h) IllegalOperation "hDuplicateTo"
315+ "handles are incompatible" Nothing Nothing)
src/Development/IDE/GHC/Warnings.hs+8−6
@@ -1,4 +1,4 @@
1−-- Copyright (c) 2019 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
1+-- Copyright (c) 2019 The DAML Authors. All rights reserved.
2-- SPDX-License-Identifier: Apache-2.0
2-- SPDX-License-Identifier: Apache-2.0
3
3
4module Development.IDE.GHC.Warnings(withWarnings) where
4module Development.IDE.GHC.Warnings(withWarnings) where
@@ -9,6 +9,7 @@ import GhcPlugins as GHC hiding (Var)
9
9
10import Control.Concurrent.Extra
10import Control.Concurrent.Extra
11import Control.Monad.Extra
11import Control.Monad.Extra
12+import qualified Data.Text as T
12
13
13import Development.IDE.Types.Diagnostics
14import Development.IDE.Types.Diagnostics
14import Development.IDE.GHC.Util
15import Development.IDE.GHC.Util
@@ -24,13 +25,14 @@ import Development.IDE.GHC.Error
24-- https://github.com/ghc/ghc/blob/5f1d949ab9e09b8d95319633854b7959df06eb58/compiler/main/GHC.hs#L623-L640
25-- https://github.com/ghc/ghc/blob/5f1d949ab9e09b8d95319633854b7959df06eb58/compiler/main/GHC.hs#L623-L640
25-- which basically says that log_action is taken from the ModSummary when GHC feels like it.
26-- which basically says that log_action is taken from the ModSummary when GHC feels like it.
26-- The given argument lets you refresh a ModSummary log_action
27-- The given argument lets you refresh a ModSummary log_action
27−withWarnings :: GhcMonad m => ((ModSummary -> ModSummary) -> m a) -> m ([FileDiagnostic], a)
28+withWarnings :: GhcMonad m => T.Text -> ((ModSummary -> ModSummary) -> m a) -> m ([(WarnReason, FileDiagnostic)], a)
28−withWarnings action = do
29+withWarnings diagSource action = do
29 warnings <- liftIO $ newVar []
30 warnings <- liftIO $ newVar []
30 oldFlags <- getDynFlags
31 oldFlags <- getDynFlags
32+ let newAction :: DynFlags -> WarnReason -> Severity -> SrcSpan -> PprStyle -> SDoc -> IO ()
31− let newAction dynFlags _ _ loc _ msg = do
33+ newAction dynFlags wr _ loc style msg = do
32− let d = diagFromErrMsg dynFlags $ mkPlainWarnMsg dynFlags loc msg
34+ let wr_d = fmap (wr,) $ diagFromErrMsg diagSource dynFlags $ mkWarnMsg dynFlags loc (queryQual style) msg
33− modifyVar_ warnings $ return . (d:)
35+ modifyVar_ warnings $ return . (wr_d:)
34 setLogAction newAction
36 setLogAction newAction
35 res <- action $ \x -> x{ms_hspp_opts = (ms_hspp_opts x){log_action = newAction}}
37 res <- action $ \x -> x{ms_hspp_opts = (ms_hspp_opts x){log_action = newAction}}
36 setLogAction $ log_action oldFlags
38 setLogAction $ log_action oldFlags
src/Development/IDE/GHC/WithDynFlags.hsnew file+30
@@ -0,0 +1,30 @@
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+222−58
@@ -1,58 +1,165 @@
1−-- Copyright (c) 2019 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
1+-- Copyright (c) 2019 The DAML Authors. All rights reserved.
2-- SPDX-License-Identifier: Apache-2.0
2-- SPDX-License-Identifier: Apache-2.0
3
3
4module Development.IDE.Import.DependencyInformation
4module Development.IDE.Import.DependencyInformation
5 ( DependencyInformation(..)
5 ( DependencyInformation(..)
6+ , ModuleImports(..)
6 , RawDependencyInformation(..)
7 , RawDependencyInformation(..)
7 , NodeError(..)
8 , NodeError(..)
8 , ModuleParseError(..)
9 , ModuleParseError(..)
9 , TransitiveDependencies(..)
10 , TransitiveDependencies(..)
11+ , FilePathId(..)
12+ , NamedModuleDep(..)
13+
14+ , PathIdMap
15+ , emptyPathIdMap
16+ , getPathId
17+ , lookupPathToId
18+ , insertImport
19+ , pathToId
20+ , idToPath
21+ , reachableModules
10 , processDependencyInformation
22 , processDependencyInformation
11 , transitiveDeps
23 , transitiveDeps
24+
25+ , BootIdMap
26+ , insertBootId
12 ) where
27 ) where
13
28
14import Control.DeepSeq
29import Control.DeepSeq
15import Data.Bifunctor
30import Data.Bifunctor
31+import Data.Coerce
32+import Data.List
33+import Data.Tuple.Extra hiding (first, second)
16import Development.IDE.GHC.Orphans()
34import Development.IDE.GHC.Orphans()
17import Data.Either
35import Data.Either
18−import Data.Foldable
19import Data.Graph
36import Data.Graph
37+import Data.HashMap.Strict (HashMap)
38+import qualified Data.HashMap.Strict as HMS
20import Data.List.NonEmpty (NonEmpty(..), nonEmpty)
39import Data.List.NonEmpty (NonEmpty(..), nonEmpty)
21import qualified Data.List.NonEmpty as NonEmpty
40import qualified Data.List.NonEmpty as NonEmpty
22−import Data.Map (Map)
41+import Data.IntMap (IntMap)
23−import qualified Data.Map.Strict as MS
42+import qualified Data.IntMap.Strict as IntMap
24−import qualified Data.Map.Lazy as ML
43+import qualified Data.IntMap.Lazy as IntMapLazy
44+import Data.IntSet (IntSet)
45+import qualified Data.IntSet as IntSet
25import Data.Maybe
46import Data.Maybe
26import Data.Set (Set)
47import Data.Set (Set)
27import qualified Data.Set as Set
48import qualified Data.Set as Set
28−import Data.Tuple.Extra (fst3)
29import GHC.Generics (Generic)
49import GHC.Generics (Generic)
30
50
31import Development.IDE.Types.Diagnostics
51import Development.IDE.Types.Diagnostics
32import Development.IDE.Types.Location
52import Development.IDE.Types.Location
53+import Development.IDE.Import.FindImports (ArtifactsLocation(..))
33
54
34import GHC
55import GHC
35import Module
56import Module
36
57
37−-- | Unprocessed results that we get from following all imports recursively starting from a module.
58+-- | The imports for a given module.
38−data RawDependencyInformation = RawDependencyInformation
59+data ModuleImports = ModuleImports
39− { moduleDependencies :: Map NormalizedFilePath (Either ModuleParseError [(Located ModuleName, Maybe NormalizedFilePath)])
60+ { moduleImports :: ![(Located ModuleName, Maybe FilePathId)]
61+ -- ^ Imports of a module in the current package and the file path of
62+ -- that module on disk (if we found it)
40− , pkgDependencies :: Map NormalizedFilePath (Set InstalledUnitId)
63+ , packageImports :: !(Set InstalledUnitId)
41− -- ^ Transitive dependencies on pkgs of this file, i.e. immidiate package dependencies and the
64+ -- ^ Transitive package dependencies unioned for all imports.
42− -- transitive package dependencies of those packages.
65+ } deriving Show
66+
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.
70+newtype FilePathId = FilePathId { getFilePathId :: Int }
71+ deriving (Show, NFData, Eq, Ord)
72+
73+-- | Map from 'FilePathId'
74+type FilePathIdMap = IntMap
75+
76+-- | Set of 'FilePathId's
77+type FilePathIdSet = IntSet
78+
79+data PathIdMap = PathIdMap
80+ { idToPathMap :: !(FilePathIdMap ArtifactsLocation)
81+ , pathToIdMap :: !(HashMap NormalizedFilePath FilePathId)
43 }
82 }
83+ deriving (Show, Generic)
84+
85+instance NFData PathIdMap
86+
87+emptyPathIdMap :: PathIdMap
88+emptyPathIdMap = PathIdMap IntMap.empty HMS.empty
89+
90+getPathId :: ArtifactsLocation -> PathIdMap -> (FilePathId, PathIdMap)
91+getPathId path m@PathIdMap{..} =
92+ case HMS.lookup (artifactFilePath path) pathToIdMap of
93+ Nothing ->
94+ let !newId = FilePathId $ HMS.size pathToIdMap
95+ in (newId, insertPathId path newId m)
96+ Just id -> (id, m)
97+
98+insertPathId :: ArtifactsLocation -> FilePathId -> PathIdMap -> PathIdMap
99+insertPathId path id PathIdMap{..} =
100+ PathIdMap (IntMap.insert (getFilePathId id) path idToPathMap) (HMS.insert (artifactFilePath path) id pathToIdMap)
101+
102+insertImport :: FilePathId -> Either ModuleParseError ModuleImports -> RawDependencyInformation -> RawDependencyInformation
103+insertImport (FilePathId k) v rawDepInfo = rawDepInfo { rawImports = IntMap.insert k v (rawImports rawDepInfo) }
104+
105+pathToId :: 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
110+
111+idToPath :: 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)
121+
122+-- | Unprocessed results that we find by following imports recursively.
123+data RawDependencyInformation = RawDependencyInformation
124+ { rawImports :: !(FilePathIdMap (Either ModuleParseError ModuleImports))
125+ , 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)
134+pkgDependencies RawDependencyInformation{..} =
135+ IntMap.map (either (const Set.empty) packageImports) rawImports
44
136
45data DependencyInformation =
137data DependencyInformation =
46 DependencyInformation
138 DependencyInformation
47− { depErrorNodes :: Map NormalizedFilePath (NonEmpty NodeError)
139+ { depErrorNodes :: !(FilePathIdMap (NonEmpty NodeError))
48 -- ^ Nodes that cannot be processed correctly.
140 -- ^ Nodes that cannot be processed correctly.
49− , depModuleDeps :: Map NormalizedFilePath (Set NormalizedFilePath)
141+ , depModuleNames :: !(FilePathIdMap ShowableModuleName)
142+ , depModuleDeps :: !(FilePathIdMap FilePathIdSet)
50 -- ^ For a non-error node, this contains the set of module immediate dependencies
143 -- ^ For a non-error node, this contains the set of module immediate dependencies
51 -- in the same package.
144 -- in the same package.
52− , depPkgDeps :: Map NormalizedFilePath (Set InstalledUnitId)
145+ , depPkgDeps :: !(FilePathIdMap (Set InstalledUnitId))
53 -- ^ For a non-error node, this contains the set of immediate pkg deps.
146 -- ^ For a non-error node, this contains the set of immediate pkg deps.
147+ , depPathIdMap :: !PathIdMap
148+ -- ^ Map from FilePath to FilePathId
149+ , depBootMap :: !BootIdMap
150+ -- ^ Map from hs-boot file to the corresponding hs file
54 } deriving (Show, Generic)
151 } deriving (Show, Generic)
55
152
153+newtype ShowableModuleName =
154+ ShowableModuleName {showableModuleName :: ModuleName}
155+ deriving NFData
156+
157+instance Show ShowableModuleName where show = moduleNameString . showableModuleName
158+
159+reachableModules :: DependencyInformation -> [NormalizedFilePath]
160+reachableModules DependencyInformation{..} =
161+ map (idToPath depPathIdMap . FilePathId) $ IntMap.keys depErrorNodes <> IntMap.keys depModuleDeps
162+
56instance NFData DependencyInformation
163instance NFData DependencyInformation
57
164
58-- | This does not contain the actual parse error as that is already reported by GetParsedModule.
165-- | This does not contain the actual parse error as that is already reported by GetParsedModule.
@@ -69,7 +176,7 @@ instance NFData LocateError
69
176
70-- | An error attached to a node in the dependency graph.
177-- | An error attached to a node in the dependency graph.
71data NodeError
178data NodeError
72− = PartOfCycle (Located ModuleName) [NormalizedFilePath]
179+ = PartOfCycle (Located ModuleName) [FilePathId]
73 -- ^ This module is part of an import cycle. The module name corresponds
180 -- ^ This module is part of an import cycle. The module name corresponds
74 -- to the import that enters the cycle starting from this module.
181 -- to the import that enters the cycle starting from this module.
75 -- The list of filepaths represents the elements
182 -- The list of filepaths represents the elements
@@ -94,12 +201,12 @@ instance NFData NodeError where
94-- `ErrorNode`. Otherwise it is a `SuccessNode`.
201-- `ErrorNode`. Otherwise it is a `SuccessNode`.
95data NodeResult
202data NodeResult
96 = ErrorNode (NonEmpty NodeError)
203 = ErrorNode (NonEmpty NodeError)
97− | SuccessNode [(Located ModuleName, NormalizedFilePath)]
204+ | SuccessNode [(Located ModuleName, FilePathId)]
98 deriving Show
205 deriving Show
99
206
100partitionNodeResults
207partitionNodeResults
101 :: [(a, NodeResult)]
208 :: [(a, NodeResult)]
102− -> ([(a, NonEmpty NodeError)], [(a, [(Located ModuleName, NormalizedFilePath)])])
209+ -> ([(a, NonEmpty NodeError)], [(a, [(Located ModuleName, FilePathId)])])
103partitionNodeResults = partitionEithers . map f
210partitionNodeResults = partitionEithers . map f
104 where f (a, ErrorNode errs) = Left (a, errs)
211 where f (a, ErrorNode errs) = Left (a, errs)
105 f (a, SuccessNode imps) = Right (a, imps)
212 f (a, SuccessNode imps) = Right (a, imps)
@@ -111,102 +218,159 @@ instance Semigroup NodeResult where
111 SuccessNode a <> SuccessNode _ = SuccessNode a
218 SuccessNode a <> SuccessNode _ = SuccessNode a
112
219
113processDependencyInformation :: RawDependencyInformation -> DependencyInformation
220processDependencyInformation :: RawDependencyInformation -> DependencyInformation
114−processDependencyInformation rawResults =
221+processDependencyInformation rawDepInfo@RawDependencyInformation{..} =
115 DependencyInformation
222 DependencyInformation
116− { depErrorNodes = MS.mapMaybe errorNode resultGraph
223+ { depErrorNodes = IntMap.fromList errorNodes
117 , depModuleDeps = moduleDeps
224 , depModuleDeps = moduleDeps
225+ , depModuleNames = IntMap.fromList $ coerce moduleNames
118− , depPkgDeps = pkgDependencies rawResults
226+ , depPkgDeps = pkgDependencies rawDepInfo
227+ , depPathIdMap = rawPathIdMap
228+ , depBootMap = rawBootMap
119 }
229 }
120− where resultGraph = buildResultGraph rawResults
230+ where resultGraph = buildResultGraph rawImports
121− successEdges :: [(NormalizedFilePath, NormalizedFilePath, [NormalizedFilePath])]
231+ (errorNodes, successNodes) = partitionNodeResults $ IntMap.toList resultGraph
232+ moduleNames :: [(FilePathId, ModuleName)]
233+ moduleNames =
234+ [ (fId, modName) | (_, imports) <- successNodes, (L _ modName, fId) <- imports]
235+ successEdges :: [(FilePathId, FilePathId, [FilePathId])]
122− successEdges = map (\(k,ks) -> (k,k,ks)) $ MS.toList $
236+ successEdges =
123− MS.map (map snd) $ MS.mapMaybe successNode resultGraph
237+ map
238+ (\(file, imports) -> (FilePathId file, FilePathId file, map snd imports))
239+ successNodes
124 moduleDeps =
240 moduleDeps =
241+ IntMap.fromList $
125− MS.fromList $ map (\(_, v, vs) -> (v, Set.fromList vs)) successEdges
242+ map (\(_, FilePathId v, vs) -> (v, IntSet.fromList $ coerce vs))
126− errorNode (ErrorNode errs) = Just errs
243+ successEdges
127− errorNode _ = Nothing
128− successNode (SuccessNode fs) = Just fs
129− successNode _ = Nothing
130
244
131-- | Given a dependency graph, buildResultGraph detects and propagates errors in that graph as follows:
245-- | Given a dependency graph, buildResultGraph detects and propagates errors in that graph as follows:
132-- 1. Mark each node that is part of an import cycle as an error node.
246-- 1. Mark each node that is part of an import cycle as an error node.
133-- 2. Mark each node that has a parse error as an error node.
247-- 2. Mark each node that has a parse error as an error node.
134-- 3. Mark each node whose immediate children could not be located as an error.
248-- 3. Mark each node whose immediate children could not be located as an error.
135-- 4. Recursively propagate errors to parents if they are not already error nodes.
249-- 4. Recursively propagate errors to parents if they are not already error nodes.
136−buildResultGraph :: RawDependencyInformation -> Map NormalizedFilePath NodeResult
250+buildResultGraph :: FilePathIdMap (Either ModuleParseError ModuleImports) -> FilePathIdMap NodeResult
137buildResultGraph g = propagatedErrors
251buildResultGraph g = propagatedErrors
138 where
252 where
139 sccs = stronglyConnComp (graphEdges g)
253 sccs = stronglyConnComp (graphEdges g)
140 (_, cycles) = partitionSCC sccs
254 (_, cycles) = partitionSCC sccs
141− cycleErrors :: Map NormalizedFilePath NodeResult
255+ cycleErrors :: IntMap NodeResult
142− cycleErrors = MS.unionsWith (<>) $ map errorsForCycle cycles
256+ cycleErrors = IntMap.unionsWith (<>) $ map errorsForCycle cycles
143− errorsForCycle :: [NormalizedFilePath] -> Map NormalizedFilePath NodeResult
257+ errorsForCycle :: [FilePathId] -> IntMap NodeResult
144 errorsForCycle files =
258 errorsForCycle files =
145− MS.fromListWith (<>) (concatMap (cycleErrorsForFile files) files)
259+ IntMap.fromListWith (<>) $ coerce $ concatMap (cycleErrorsForFile files) files
146− cycleErrorsForFile :: [NormalizedFilePath] -> NormalizedFilePath -> [(NormalizedFilePath,NodeResult)]
260+ cycleErrorsForFile :: [FilePathId] -> FilePathId -> [(FilePathId,NodeResult)]
147 cycleErrorsForFile cycle f =
261 cycleErrorsForFile cycle f =
148 let entryPoints = mapMaybe (findImport f) cycle
262 let entryPoints = mapMaybe (findImport f) cycle
149 in map (\imp -> (f, ErrorNode (PartOfCycle imp cycle :| []))) entryPoints
263 in map (\imp -> (f, ErrorNode (PartOfCycle imp cycle :| []))) entryPoints
150− otherErrors = MS.map otherErrorsForFile (moduleDependencies g)
264+ otherErrors = IntMap.map otherErrorsForFile g
151− otherErrorsForFile :: Either ModuleParseError [(Located ModuleName, Maybe NormalizedFilePath)] -> NodeResult
265+ otherErrorsForFile :: Either ModuleParseError ModuleImports -> NodeResult
152 otherErrorsForFile (Left err) = ErrorNode (ParseError err :| [])
266 otherErrorsForFile (Left err) = ErrorNode (ParseError err :| [])
153− otherErrorsForFile (Right imports) =
267+ otherErrorsForFile (Right ModuleImports{moduleImports}) =
154 let toEither (imp, Nothing) = Left imp
268 let toEither (imp, Nothing) = Left imp
155 toEither (imp, Just path) = Right (imp, path)
269 toEither (imp, Just path) = Right (imp, path)
156− (errs, imports') = partitionEithers (map toEither imports)
270+ (errs, imports') = partitionEithers (map toEither moduleImports)
157 in case nonEmpty errs of
271 in case nonEmpty errs of
158 Nothing -> SuccessNode imports'
272 Nothing -> SuccessNode imports'
159 Just errs' -> ErrorNode (NonEmpty.map FailedToLocateImport errs')
273 Just errs' -> ErrorNode (NonEmpty.map FailedToLocateImport errs')
160
274
161− unpropagatedErrors = MS.unionWith (<>) cycleErrors otherErrors
275+ unpropagatedErrors = IntMap.unionWith (<>) cycleErrors otherErrors
162 -- The recursion here is fine since we use a lazy map and
276 -- The recursion here is fine since we use a lazy map and
163 -- we only recurse on SuccessNodes. In particular, we do not recurse
277 -- we only recurse on SuccessNodes. In particular, we do not recurse
164 -- on nodes that are part of a cycle as they are already marked as
278 -- on nodes that are part of a cycle as they are already marked as
165 -- error nodes.
279 -- error nodes.
166 propagatedErrors =
280 propagatedErrors =
167− ML.map propagate unpropagatedErrors
281+ IntMapLazy.map propagate unpropagatedErrors
168 propagate :: NodeResult -> NodeResult
282 propagate :: NodeResult -> NodeResult
169 propagate n@(ErrorNode _) = n
283 propagate n@(ErrorNode _) = n
170 propagate n@(SuccessNode imps) =
284 propagate n@(SuccessNode imps) =
171− let results = map (\(imp, dep) -> (imp, propagatedErrors MS.! dep)) imps
285+ let results = map (\(imp, FilePathId dep) -> (imp, propagatedErrors IntMap.! dep)) imps
172 (errs, _) = partitionNodeResults results
286 (errs, _) = partitionNodeResults results
173 in case nonEmpty errs of
287 in case nonEmpty errs of
174 Nothing -> n
288 Nothing -> n
175 Just errs' -> ErrorNode (NonEmpty.map (ParentOfErrorNode . fst) errs')
289 Just errs' -> ErrorNode (NonEmpty.map (ParentOfErrorNode . fst) errs')
176− findImport :: NormalizedFilePath -> NormalizedFilePath -> Maybe (Located ModuleName)
290+ findImport :: FilePathId -> FilePathId -> Maybe (Located ModuleName)
177− findImport file importedFile =
291+ findImport (FilePathId file) importedFile =
178− case moduleDependencies g MS.! file of
292+ case g IntMap.! file of
179 Left _ -> error "Tried to call findImport on a module with a parse error"
293 Left _ -> error "Tried to call findImport on a module with a parse error"
180− Right imports ->
294+ Right ModuleImports{moduleImports} ->
181− fmap fst $ find (\(_, resolvedImp) -> resolvedImp == Just importedFile) imports
295+ fmap fst $ find (\(_, resolvedImp) -> resolvedImp == Just importedFile) moduleImports
182
296
183−graphEdges :: RawDependencyInformation -> [(NormalizedFilePath, NormalizedFilePath, [NormalizedFilePath])]
297+graphEdges :: FilePathIdMap (Either ModuleParseError ModuleImports) -> [(FilePathId, FilePathId, [FilePathId])]
184graphEdges g =
298graphEdges g =
185− map (\(k, ks) -> (k, k, ks)) $ MS.toList $ MS.map deps $ moduleDependencies g
299+ map (\(k, v) -> (FilePathId k, FilePathId k, deps v)) $ IntMap.toList g
186− where deps :: Either e [(i, Maybe NormalizedFilePath)] -> [NormalizedFilePath]
300+ where deps :: Either e ModuleImports -> [FilePathId]
187 deps (Left _) = []
301 deps (Left _) = []
188− deps (Right imports) = mapMaybe snd imports
302+ deps (Right ModuleImports{moduleImports}) = mapMaybe snd moduleImports
189
303
190partitionSCC :: [SCC a] -> ([a], [[a]])
304partitionSCC :: [SCC a] -> ([a], [[a]])
191partitionSCC (CyclicSCC xs:rest) = second (xs:) $ partitionSCC rest
305partitionSCC (CyclicSCC xs:rest) = second (xs:) $ partitionSCC rest
192partitionSCC (AcyclicSCC x:rest) = first (x:) $ partitionSCC rest
306partitionSCC (AcyclicSCC x:rest) = first (x:) $ partitionSCC rest
193partitionSCC [] = ([], [])
307partitionSCC [] = ([], [])
194
308
309+
195transitiveDeps :: DependencyInformation -> NormalizedFilePath -> Maybe TransitiveDependencies
310transitiveDeps :: DependencyInformation -> NormalizedFilePath -> Maybe TransitiveDependencies
196−transitiveDeps DependencyInformation{..} f = do
311+transitiveDeps DependencyInformation{..} file = do
312+ let !fileId = pathToId depPathIdMap file
313+ reachableVs <-
314+ -- Delete the starting node
315+ IntSet.delete (getFilePathId fileId) .
197− reachableVs <- Set.delete f . Set.fromList . map (fst3 . fromVertex) . reachable g <$> toVertex f
316+ IntSet.fromList . map (fst3 . fromVertex) .
317+ reachable g <$> toVertex (getFilePathId fileId)
318+ let transitiveModuleDepIds =
198− let transitiveModuleDeps = filter (\v -> v `Set.member` reachableVs) $ map (fst3 . fromVertex) vs
319+ filter (\v -> v `IntSet.member` reachableVs) $ map (fst3 . fromVertex) vs
320+ let transitivePkgDeps =
321+ Set.toList $ Set.unions $
199− let transitivePkgDeps = Set.toList $ foldMap (\f -> MS.findWithDefault Set.empty f depPkgDeps) (f : transitiveModuleDeps)
322+ map (\f -> IntMap.findWithDefault Set.empty f depPkgDeps) $
323+ 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+ ]
200 pure TransitiveDependencies {..}
331 pure TransitiveDependencies {..}
332+ where
201− where (g, fromVertex, toVertex) = graphFromEdges (map (\(f, fs) -> (f, f, Set.toList fs)) $ MS.toList depModuleDeps)
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+
202− vs = topSort g
342+ vs = topSort g
203
343
204data TransitiveDependencies = TransitiveDependencies
344data TransitiveDependencies = TransitiveDependencies
205 { transitiveModuleDeps :: [NormalizedFilePath]
345 { transitiveModuleDeps :: [NormalizedFilePath]
206 -- ^ Transitive module dependencies in topological order.
346 -- ^ Transitive module dependencies in topological order.
207 -- The module itself is not included.
347 -- The module itself is not included.
348+ , transitiveNamedModuleDeps :: [NamedModuleDep]
349+ -- ^ Transitive module dependencies in topological order.
350+ -- The module itself is not included.
208 , transitivePkgDeps :: [InstalledUnitId]
351 , transitivePkgDeps :: [InstalledUnitId]
209 -- ^ Transitive pkg dependencies in unspecified order.
352 -- ^ Transitive pkg dependencies in unspecified order.
210 } deriving (Eq, Show, Generic)
353 } deriving (Eq, Show, Generic)
211
354
212instance NFData TransitiveDependencies
355instance 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+95−72
@@ -1,26 +1,26 @@
1−-- Copyright (c) 2019 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
1+-- Copyright (c) 2019 The DAML Authors. All rights reserved.
2-- SPDX-License-Identifier: Apache-2.0
2-- SPDX-License-Identifier: Apache-2.0
3
3
4−{-# LANGUAGE OverloadedStrings #-}
4+{-# LANGUAGE CPP #-}
5+#include "ghc-api-version.h"
5
6
6module Development.IDE.Import.FindImports
7module Development.IDE.Import.FindImports
7− ( getImportsParsed
8− , locateModule
8+ ( locateModule
9 , Import(..)
9 , Import(..)
10+ , ArtifactsLocation(..)
11+ , modSummaryToArtifactsLocation
12+ , isBootLocation
13+ , mkImportDirs
10 ) where
14 ) where
11
15
12import Development.IDE.GHC.Error as ErrUtils
16import Development.IDE.GHC.Error as ErrUtils
13import Development.IDE.GHC.Orphans()
17import Development.IDE.GHC.Orphans()
14import Development.IDE.Types.Diagnostics
18import Development.IDE.Types.Diagnostics
15import Development.IDE.Types.Location
19import Development.IDE.Types.Location
20+import Development.IDE.GHC.Compat
16-- GHC imports
21-- GHC imports
17−import BasicTypes (StringLiteral(..))
18−import DynFlags
19import FastString
22import FastString
20−import GHC
21−import qualified HeaderInfo as Hdr
22import qualified Module as M
23import qualified Module as M
23−import qualified GHC.LanguageExtensions.Type as GHC
24import Packages
24import Packages
25import Outputable (showSDoc, ppr, pprPanic)
25import Outputable (showSDoc, ppr, pprPanic)
26import Finder
26import Finder
@@ -29,81 +29,108 @@ import Control.DeepSeq
29-- standard imports
29-- standard imports
30import Control.Monad.Extra
30import Control.Monad.Extra
31import Control.Monad.IO.Class
31import Control.Monad.IO.Class
32−import qualified Control.Monad.Trans.Except as Ex
33import System.FilePath
32import System.FilePath
33+import DriverPhases
34+import Data.Maybe
34
35
35data Import
36data Import
36− = FileImport !NormalizedFilePath
37+ = FileImport !ArtifactsLocation
37 | PackageImport !M.InstalledUnitId
38 | PackageImport !M.InstalledUnitId
38 deriving (Show)
39 deriving (Show)
39
40
40−instance NFData Import where
41+data ArtifactsLocation = ArtifactsLocation
41− rnf (FileImport x) = rnf x
42+ { artifactFilePath :: !NormalizedFilePath
42− rnf (PackageImport x) = rnf x
43+ , artifactModLocation :: !ModLocation
43−
44+ , artifactIsSource :: !Bool -- ^ True if a module is a source input
45+ }
46+ deriving (Show)
44
47
45−-- | GhcMonad function to chase imports of a module given as a StringBuffer. Returns given module's
48+instance NFData ArtifactsLocation where
46−-- name and its imports.
49+ rnf ArtifactsLocation{..} = rnf artifactFilePath `seq` rwhnf artifactModLocation `seq` rnf artifactIsSource
47−getImportsParsed :: Monad m =>
48− DynFlags ->
49− GHC.ParsedSource ->
50− Ex.ExceptT [FileDiagnostic] m
51− (M.ModuleName, [(Maybe FastString, Located M.ModuleName)])
52−getImportsParsed dflags (L loc parsed) = do
53− let modName = maybe (GHC.mkModuleName "Main") GHC.unLoc $ GHC.hsmodName parsed
54
50
55− -- refuse source imports
51+isBootLocation :: ArtifactsLocation -> Bool
56− let srcImports = filter (ideclSource . GHC.unLoc) $ GHC.hsmodImports parsed
52+isBootLocation = not . artifactIsSource
57− when (not $ null srcImports) $ Ex.throwE $
58− concat
59− [ diagFromString mloc ("Illegal source import of " <> GHC.moduleNameString (GHC.unLoc $ GHC.ideclName i))
60− | L mloc i <- srcImports ]
61
53
62− -- most of these corner cases are also present in https://hackage.haskell.org/package/ghc-8.6.1/docs/src/HeaderInfo.html#getImports
54+instance NFData Import where
63− -- but we want to avoid parsing the module twice
55+ rnf (FileImport x) = rnf x
64− let implicit_prelude = xopt GHC.ImplicitPrelude dflags
56+ rnf (PackageImport x) = rnf x
65− implicit_imports = Hdr.mkPrelImports modName loc implicit_prelude $ GHC.hsmodImports parsed
66
57
67− -- filter out imports that come from packages
58+modSummaryToArtifactsLocation :: NormalizedFilePath -> ModSummary -> ArtifactsLocation
68− return (modName, [(fmap sl_fs $ ideclPkgQual i, ideclName i)
59+modSummaryToArtifactsLocation nfp ms = ArtifactsLocation nfp (ms_location ms) (isSource (ms_hsc_src ms))
69− | i <- map GHC.unLoc $ implicit_imports ++ GHC.hsmodImports parsed
60+ where
70− , GHC.moduleNameString (GHC.unLoc $ ideclName i) /= "GHC.Prim"
61+ isSource HsSrcFile = True
71− ])
62+ isSource _ = False
72
63
73
64
74-- | locate a module in the file system. Where we go from *daml to Haskell
65-- | locate a module in the file system. Where we go from *daml to Haskell
75locateModuleFile :: MonadIO m
66locateModuleFile :: MonadIO m
76− => DynFlags
67+ => [[FilePath]]
77 -> [String]
68 -> [String]
78 -> (NormalizedFilePath -> m Bool)
69 -> (NormalizedFilePath -> m Bool)
70+ -> Bool
79 -> ModuleName
71 -> ModuleName
80 -> m (Maybe NormalizedFilePath)
72 -> m (Maybe NormalizedFilePath)
81−locateModuleFile dflags exts doesExist modName = do
73+locateModuleFile import_dirss exts doesExist isSource modName = do
74+ let candidates import_dirs =
82− let candidates = [ toNormalizedFilePath (prefix </> M.moduleNameSlashes modName <.> ext) | prefix <- importPaths dflags, ext <- exts]
75+ [ toNormalizedFilePath' (prefix </> M.moduleNameSlashes modName <.> maybeBoot ext)
76+ | prefix <- import_dirs , ext <- exts]
83− findM doesExist candidates
77+ findM doesExist (concatMap candidates import_dirss)
78+ where
79+ maybeBoot ext
80+ | isSource = ext ++ "-boot"
81+ | otherwise = ext
82+
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
84
89
85-- | locate a module in either the file system or the package database. Where we go from *daml to
90-- | locate a module in either the file system or the package database. Where we go from *daml to
86-- Haskell
91-- Haskell
87locateModule
92locateModule
88 :: MonadIO m
93 :: MonadIO m
89 => DynFlags
94 => DynFlags
95+ -> [(M.InstalledUnitId, DynFlags)] -- Sets import directories to look in
90 -> [String]
96 -> [String]
91 -> (NormalizedFilePath -> m Bool)
97 -> (NormalizedFilePath -> m Bool)
92 -> Located ModuleName
98 -> Located ModuleName
93 -> Maybe FastString
99 -> Maybe FastString
100+ -> Bool
94 -> m (Either [FileDiagnostic] Import)
101 -> m (Either [FileDiagnostic] Import)
95−locateModule dflags exts doesExist modName mbPkgName = do
102+locateModule dflags comp_info exts doesExist modName mbPkgName isSource = do
96 case mbPkgName of
103 case mbPkgName of
104+ -- "this" means that we should only look in the current package
105+ Just "this" -> do
106+ lookupLocal [importPaths dflags]
97 -- if a package name is given we only go look for a package
107 -- 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]
98− Just _pkgName -> lookupInPackageDB dflags
111+ | otherwise -> lookupInPackageDB dflags
99 Nothing -> do
112 Nothing -> do
100 -- first try to find the module as a file. If we can't find it try to find it in the package
113 -- first try to find the module as a file. If we can't find it try to find it in the package
101 -- database.
114 -- 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.
102− mbFile <- locateModuleFile dflags exts doesExist $ unLoc modName
118+ mbFile <- locateModuleFile (importPaths dflags : map snd import_paths) exts doesExist isSource $ unLoc modName
103 case mbFile of
119 case mbFile of
104 Nothing -> lookupInPackageDB dflags
120 Nothing -> lookupInPackageDB dflags
105− Just file -> return $ Right $ FileImport file
121+ Just file -> toModLocation file
106 where
122 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+
107 lookupInPackageDB dfs =
134 lookupInPackageDB dfs =
108 case lookupModuleWithSuggestions dfs (unLoc modName) mbPkgName of
135 case lookupModuleWithSuggestions dfs (unLoc modName) mbPkgName of
109 LookupFound _m pkgConfig -> return $ Right $ PackageImport $ unitId pkgConfig
136 LookupFound _m pkgConfig -> return $ Right $ PackageImport $ unitId pkgConfig
@@ -114,7 +141,7 @@ notFoundErr :: DynFlags -> Located M.ModuleName -> LookupResult -> [FileDiagnost
114notFoundErr dfs modName reason =
141notFoundErr dfs modName reason =
115 mkError' $ ppr' $ cannotFindModule dfs modName0 $ lookupToFindResult reason
142 mkError' $ ppr' $ cannotFindModule dfs modName0 $ lookupToFindResult reason
116 where
143 where
117− mkError' = diagFromString (getLoc modName)
144+ mkError' = diagFromString "not found" DsError (getLoc modName)
118 modName0 = unLoc modName
145 modName0 = unLoc modName
119 ppr' = showSDoc dfs
146 ppr' = showSDoc dfs
120 -- We convert the lookup result to a find result to reuse GHC's cannotFindMoudle pretty printer.
147 -- We convert the lookup result to a find result to reuse GHC's cannotFindMoudle pretty printer.
@@ -122,35 +149,31 @@ notFoundErr dfs modName reason =
122 \case
149 \case
123 LookupFound _m _pkgConfig ->
150 LookupFound _m _pkgConfig ->
124 pprPanic "Impossible: called lookupToFind on found module." (ppr modName0)
151 pprPanic "Impossible: called lookupToFind on found module." (ppr modName0)
125− LookupMultiple rs -> (FoundMultiple rs)
152+ LookupMultiple rs -> FoundMultiple rs
126 LookupHidden pkg_hiddens mod_hiddens ->
153 LookupHidden pkg_hiddens mod_hiddens ->
127− (NotFound
154+ notFound
128− { fr_paths = []
129− , fr_pkg = Nothing
130− , fr_pkgs_hidden = map (moduleUnitId . fst) pkg_hiddens
155+ { fr_pkgs_hidden = map (moduleUnitId . fst) pkg_hiddens
131 , fr_mods_hidden = map (moduleUnitId . fst) mod_hiddens
156 , fr_mods_hidden = map (moduleUnitId . fst) mod_hiddens
132− , fr_unusables = []
133− , fr_suggestions = []
134− })
157+ }
158+#if MIN_GHC_API_VERSION(8,6,0)
135 LookupUnusable unusable ->
159 LookupUnusable unusable ->
136 let unusables' = map get_unusable unusable
160 let unusables' = map get_unusable unusable
137 get_unusable (m, ModUnusable r) = (moduleUnitId m, r)
161 get_unusable (m, ModUnusable r) = (moduleUnitId m, r)
138 get_unusable (_, r) =
162 get_unusable (_, r) =
139 pprPanic "findLookupResult: unexpected origin" (ppr r)
163 pprPanic "findLookupResult: unexpected origin" (ppr r)
140− in (NotFound
141− { fr_paths = []
142− , fr_pkg = Nothing
143− , fr_pkgs_hidden = []
144− , fr_mods_hidden = []
145− , fr_unusables = unusables'
164+ in notFound {fr_unusables = unusables'}
146− , fr_suggestions = []
165+#endif
147− })
148 LookupNotFound suggest ->
166 LookupNotFound suggest ->
167+ notFound {fr_suggestions = suggest}
168+
169+notFound :: FindResult
149− (NotFound
170+notFound = NotFound
150− { fr_paths = []
171+ { fr_paths = []
151− , fr_pkg = Nothing
172+ , fr_pkg = Nothing
152− , fr_pkgs_hidden = []
173+ , fr_pkgs_hidden = []
153− , fr_mods_hidden = []
174+ , fr_mods_hidden = []
154− , fr_unusables = []
175+#if MIN_GHC_API_VERSION(8,6,0)
155− , fr_suggestions = suggest
176+ , fr_unusables = []
156− })
177+#endif
178+ , fr_suggestions = []
179+ }
src/Development/IDE/LSP/Definition.hsdeleted−44
@@ -1,44 +0,0 @@
1−-- Copyright (c) 2019 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
2−-- SPDX-License-Identifier: Apache-2.0
3−
4−{-# LANGUAGE OverloadedStrings #-}
5−
6−-- | Go to the definition of a variable.
7−module Development.IDE.LSP.Definition
8− ( setHandlersDefinition
9− ) where
10−
11−import Language.Haskell.LSP.Types
12−import Development.IDE.Types.Location
13−
14−import Development.IDE.Types.Logger
15−import Development.IDE.Core.Rules
16−import Development.IDE.Core.Service
17−import Development.IDE.LSP.Server
18−import qualified Language.Haskell.LSP.Core as LSP
19−import Language.Haskell.LSP.Messages
20−
21−import qualified Data.Text as T
22−
23−-- | Go to the definition of a variable.
24−gotoDefinition
25− :: IdeState
26− -> TextDocumentPositionParams
27− -> IO LocationResponseParams
28−gotoDefinition ide (TextDocumentPositionParams (TextDocumentIdentifier uri) pos) = do
29− mbResult <- case uriToFilePath' uri of
30− Just path -> do
31− logInfo (ideLogger ide) $
32− "Definition request at position " <> T.pack (showPosition pos) <>
33− " in file: " <> T.pack path
34− runAction ide $ getDefinition (toNormalizedFilePath path) pos
35− Nothing -> pure Nothing
36− pure $ case mbResult of
37− Nothing -> MultiLoc []
38− Just loc -> SingleLoc loc
39−
40−
41−setHandlersDefinition :: PartialHandlers
42−setHandlersDefinition = PartialHandlers $ \WithMessage{..} x -> return x{
43− LSP.definitionHandler = withResponse RspDefinition gotoDefinition
44− }
src/Development/IDE/LSP/Hover.hsdeleted−48
@@ -1,48 +0,0 @@
1−-- Copyright (c) 2019 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
2−-- SPDX-License-Identifier: Apache-2.0
3−
4−{-# LANGUAGE OverloadedStrings #-}
5−
6−-- | Display information on hover.
7−module Development.IDE.LSP.Hover
8− ( setHandlersHover
9− ) where
10−
11−import Language.Haskell.LSP.Types
12−import Development.IDE.Types.Location
13−import Development.IDE.Core.Service
14−import Development.IDE.LSP.Server
15−import Development.IDE.Types.Logger
16−import qualified Language.Haskell.LSP.Core as LSP
17−import Language.Haskell.LSP.Messages
18−
19−import qualified Data.Text as T
20−
21−import Development.IDE.Core.Rules
22−
23−-- | Display information on hover.
24−onHover
25− :: IdeState
26− -> TextDocumentPositionParams
27− -> IO (Maybe Hover)
28−onHover ide (TextDocumentPositionParams (TextDocumentIdentifier uri) pos) = do
29− mbResult <- case uriToFilePath' uri of
30− Just (toNormalizedFilePath -> filePath) -> do
31− logInfo (ideLogger ide) $
32− "Hover request at position " <> T.pack (showPosition pos) <>
33− " in file: " <> T.pack (fromNormalizedFilePath filePath)
34− runAction ide $ getAtPoint filePath pos
35− Nothing -> pure Nothing
36−
37− case mbResult of
38− Just (mbRange, contents) ->
39− pure $ Just $ Hover
40− (HoverContents $ MarkupContent MkMarkdown $ T.intercalate sectionSeparator contents)
41− mbRange
42−
43− Nothing -> pure Nothing
44−
45−setHandlersHover :: PartialHandlers
46−setHandlersHover = PartialHandlers $ \WithMessage{..} x -> return x{
47− LSP.hoverHandler = withResponse RspHover onHover
48− }
src/Development/IDE/LSP/HoverDefinition.hsnew file+62
@@ -0,0 +1,62 @@
1+-- Copyright (c) 2019 The DAML Authors. All rights reserved.
2+-- SPDX-License-Identifier: Apache-2.0
3+
4+
5+-- | Display information on hover.
6+module Development.IDE.LSP.HoverDefinition
7+ ( setHandlersHover
8+ , setHandlersDefinition
9+ -- * For haskell-language-server
10+ , hover
11+ , gotoDefinition
12+ ) where
13+
14+import Development.IDE.Core.Rules
15+import Development.IDE.Core.Service
16+import Development.IDE.LSP.Server
17+import Development.IDE.Types.Location
18+import Development.IDE.Types.Logger
19+import Development.Shake
20+import qualified Language.Haskell.LSP.Core as LSP
21+import Language.Haskell.LSP.Messages
22+import Language.Haskell.LSP.Types
23+
24+import qualified Data.Text as T
25+
26+gotoDefinition :: IdeState -> TextDocumentPositionParams -> IO (Either ResponseError LocationResponseParams)
27+hover :: IdeState -> TextDocumentPositionParams -> IO (Either ResponseError (Maybe Hover))
28+gotoDefinition = request "Definition" getDefinition (MultiLoc []) SingleLoc
29+hover = request "Hover" getAtPoint Nothing foundHover
30+
31+foundHover :: (Maybe Range, [T.Text]) -> Maybe Hover
32+foundHover (mbRange, contents) =
33+ Just $ Hover (HoverContents $ MarkupContent MkMarkdown $ T.intercalate sectionSeparator contents) mbRange
34+
35+setHandlersDefinition, setHandlersHover :: PartialHandlers c
36+setHandlersDefinition = PartialHandlers $ \WithMessage{..} x ->
37+ return x{LSP.definitionHandler = withResponse RspDefinition $ const gotoDefinition}
38+setHandlersHover = PartialHandlers $ \WithMessage{..} x ->
39+ return x{LSP.hoverHandler = withResponse RspHover $ const hover}
40+
41+-- | Respond to and log a hover or go-to-definition request
42+request
43+ :: T.Text
44+ -> (NormalizedFilePath -> Position -> Action (Maybe a))
45+ -> b
46+ -> (a -> b)
47+ -> IdeState
48+ -> TextDocumentPositionParams
49+ -> IO (Either ResponseError b)
50+request label getResults notFound found ide (TextDocumentPositionParams (TextDocumentIdentifier uri) pos _) = do
51+ mbResult <- case uriToFilePath' uri of
52+ Just path -> logAndRunRequest label getResults ide pos path
53+ Nothing -> pure Nothing
54+ pure $ Right $ maybe notFound found mbResult
55+
56+logAndRunRequest :: T.Text -> (NormalizedFilePath -> Position -> Action b) -> IdeState -> Position -> String -> IO b
57+logAndRunRequest label getResults ide pos path = do
58+ let filePath = toNormalizedFilePath' path
59+ logInfo (ideLogger ide) $
60+ label <> " request at position " <> T.pack (showPosition pos) <>
61+ " in file: " <> T.pack path
62+ runAction ide $ getResults filePath pos
src/Development/IDE/LSP/LanguageServer.hs+154−39
@@ -1,48 +1,57 @@
1−-- Copyright (c) 2019 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
1+-- Copyright (c) 2019 The DAML Authors. All rights reserved.
2-- SPDX-License-Identifier: Apache-2.0
2-- SPDX-License-Identifier: Apache-2.0
3
3
4−{-# LANGUAGE OverloadedStrings #-}
5{-# LANGUAGE ExistentialQuantification #-}
4{-# LANGUAGE ExistentialQuantification #-}
6
5
7−-- WARNING: A copy of DA.Service.Daml.LanguageServer, try to keep them in sync
6+-- WARNING: A copy of DA.Daml.LanguageServer, try to keep them in sync
8-- This version removes the daml: handling
7-- This version removes the daml: handling
9module Development.IDE.LSP.LanguageServer
8module Development.IDE.LSP.LanguageServer
10 ( runLanguageServer
9 ( runLanguageServer
11 ) where
10 ) where
12
11
13import Language.Haskell.LSP.Types
12import Language.Haskell.LSP.Types
13+import Language.Haskell.LSP.Types.Capabilities
14import Development.IDE.LSP.Server
14import Development.IDE.LSP.Server
15+import qualified Development.IDE.GHC.Util as Ghcide
15import qualified Language.Haskell.LSP.Control as LSP
16import qualified Language.Haskell.LSP.Control as LSP
16import qualified Language.Haskell.LSP.Core as LSP
17import qualified Language.Haskell.LSP.Core as LSP
17import Control.Concurrent.Chan
18import Control.Concurrent.Chan
18import Control.Concurrent.Extra
19import Control.Concurrent.Extra
19import Control.Concurrent.Async
20import Control.Concurrent.Async
21+import Control.Concurrent.STM
22+import Control.Exception.Safe
20import Data.Default
23import Data.Default
21import Data.Maybe
24import Data.Maybe
25+import qualified Data.Set as Set
26+import qualified Data.Text as T
22−import GHC.IO.Handle (hDuplicate, hDuplicateTo)
27+import GHC.IO.Handle (hDuplicate)
23import System.IO
28import System.IO
24import Control.Monad.Extra
29import Control.Monad.Extra
25
30
26−import Development.IDE.LSP.Definition
31+import Development.IDE.Core.IdeConfiguration
32+import Development.IDE.Core.Shake
27−import Development.IDE.LSP.Hover
33+import Development.IDE.LSP.HoverDefinition
28import Development.IDE.LSP.Notifications
34import Development.IDE.LSP.Notifications
29−import Development.IDE.Core.Service
35+import Development.IDE.LSP.Outline
36+import Development.IDE.Types.Logger
30import Development.IDE.Core.FileStore
37import Development.IDE.Core.FileStore
31import Language.Haskell.LSP.Core (LspFuncs(..))
38import Language.Haskell.LSP.Core (LspFuncs(..))
32import Language.Haskell.LSP.Messages
39import Language.Haskell.LSP.Messages
33
40
34−
35runLanguageServer
41runLanguageServer
42+ :: forall config. (Show config)
36− :: LSP.Options
43+ => LSP.Options
37− -> PartialHandlers
44+ -> PartialHandlers config
45+ -> (InitializeRequest -> Either T.Text config)
46+ -> (DidChangeConfigurationNotification -> Either T.Text config)
38− -> ((FromServerMessage -> IO ()) -> VFSHandle -> IO IdeState)
47+ -> (IO LspId -> (FromServerMessage -> IO ()) -> VFSHandle -> ClientCapabilities -> IO IdeState)
39 -> IO ()
48 -> IO ()
40−runLanguageServer options userHandlers getIdeState = do
49+runLanguageServer options userHandlers onInitialConfig onConfigChange getIdeState = do
41 -- Move stdout to another file descriptor and duplicate stderr
50 -- Move stdout to another file descriptor and duplicate stderr
42 -- to stdout. This guards against stray prints from corrupting the JSON-RPC
51 -- to stdout. This guards against stray prints from corrupting the JSON-RPC
43 -- message stream.
52 -- message stream.
44 newStdout <- hDuplicate stdout
53 newStdout <- hDuplicate stdout
45− stderr `hDuplicateTo` stdout
54+ stderr `Ghcide.hDuplicateTo'` stdout
46 hSetBuffering stderr NoBuffering
55 hSetBuffering stderr NoBuffering
47 hSetBuffering stdout NoBuffering
56 hSetBuffering stdout NoBuffering
48
57
@@ -54,68 +63,174 @@ runLanguageServer options userHandlers getIdeState = do
54
63
55 -- Send everything over a channel, since you need to wait until after initialise before
64 -- Send everything over a channel, since you need to wait until after initialise before
56 -- LspFuncs is available
65 -- LspFuncs is available
57− clientMsgChan :: Chan Message <- newChan
66+ clientMsgChan :: Chan (Message config) <- newChan
58
67
59 -- These barriers are signaled when the threads reading from these chans exit.
68 -- These barriers are signaled when the threads reading from these chans exit.
60 -- This should not happen but if it does, we will make sure that the whole server
69 -- This should not happen but if it does, we will make sure that the whole server
61 -- dies and can be restarted instead of losing threads silently.
70 -- dies and can be restarted instead of losing threads silently.
62 clientMsgBarrier <- newBarrier
71 clientMsgBarrier <- newBarrier
63
72
73+ -- The set of requests ids that we have received but not finished processing
74+ pendingRequests <- newTVarIO Set.empty
75+ -- The set of requests that have been cancelled and are also in pendingRequests
76+ cancelledRequests <- newTVarIO Set.empty
77+
78+ let withResponse wrap f = Just $ \r@RequestMessage{_id} -> do
79+ atomically $ modifyTVar pendingRequests (Set.insert _id)
64− let withResponse wrap f = Just $ \r -> writeChan clientMsgChan $ Response r wrap f
80+ writeChan clientMsgChan $ Response r wrap f
65− let withNotification old f = Just $ \r -> writeChan clientMsgChan $ Notification r (\ide x -> f ide x >> whenJust old ($ r))
81+ let withNotification old f = Just $ \r -> writeChan clientMsgChan $ Notification r (\lsp ide x -> f lsp ide x >> whenJust old ($ r))
82+ let withResponseAndRequest wrap wrapNewReq f = Just $ \r@RequestMessage{_id} -> do
83+ atomically $ modifyTVar pendingRequests (Set.insert _id)
84+ writeChan clientMsgChan $ ResponseAndRequest r wrap wrapNewReq f
85+ let withInitialize f = Just $ \r -> writeChan clientMsgChan $ InitialParams r (\lsp ide x -> f lsp ide x)
86+ let cancelRequest reqId = atomically $ do
87+ queued <- readTVar pendingRequests
88+ -- We want to avoid that the list of cancelled requests
89+ -- keeps growing if we receive cancellations for requests
90+ -- that do not exist or have already been processed.
91+ when (reqId `elem` queued) $
92+ modifyTVar cancelledRequests (Set.insert reqId)
93+ let clearReqId reqId = atomically $ do
94+ modifyTVar pendingRequests (Set.delete reqId)
95+ modifyTVar cancelledRequests (Set.delete reqId)
96+ -- We implement request cancellation by racing waitForCancel against
97+ -- the actual request handler.
98+ let waitForCancel reqId = atomically $ do
99+ cancelled <- readTVar cancelledRequests
100+ unless (reqId `Set.member` cancelled) retry
66 let PartialHandlers parts =
101 let PartialHandlers parts =
102+ initializeRequestHandler <>
67 setHandlersIgnore <> -- least important
103 setHandlersIgnore <> -- least important
68− setHandlersDefinition <> setHandlersHover <> -- useful features someone may override
104+ setHandlersDefinition <> setHandlersHover <>
105+ setHandlersOutline <>
69 userHandlers <>
106 userHandlers <>
70− setHandlersNotifications -- absolutely critical, join them with user notifications
107+ setHandlersNotifications <> -- absolutely critical, join them with user notifications
108+ cancelHandler cancelRequest
109+ -- Cancel requests are special since they need to be handled
110+ -- out of order to be useful. Existing handlers are run afterwards.
71− handlers <- parts WithMessage{withResponse, withNotification} def
111+ handlers <- parts WithMessage{withResponse, withNotification, withResponseAndRequest, withInitialize} def
112+
113+ let initializeCallbacks = LSP.InitializeCallbacks
114+ { LSP.onInitialConfiguration = onInitialConfig
115+ , LSP.onConfigurationChange = onConfigChange
116+ , LSP.onStartup = handleInit (signalBarrier clientMsgBarrier ()) clearReqId waitForCancel clientMsgChan
117+ }
72
118
73 void $ waitAnyCancel =<< traverse async
119 void $ waitAnyCancel =<< traverse async
74 [ void $ LSP.runWithHandles
120 [ void $ LSP.runWithHandles
75 stdin
121 stdin
76 newStdout
122 newStdout
77− ( const $ Right ()
123+ initializeCallbacks
78− , handleInit (signalBarrier clientMsgBarrier ()) clientMsgChan
79− )
80 handlers
124 handlers
81 (modifyOptions options)
125 (modifyOptions options)
82 Nothing
126 Nothing
83 , void $ waitBarrier clientMsgBarrier
127 , void $ waitBarrier clientMsgBarrier
84 ]
128 ]
85 where
129 where
86− handleInit :: IO () -> Chan Message -> LSP.LspFuncs () -> IO (Maybe err)
130+ handleInit :: IO () -> (LspId -> IO ()) -> (LspId -> IO ()) -> Chan (Message config) -> LSP.LspFuncs config -> IO (Maybe err)
87− handleInit exitClientMsg clientMsgChan lspFuncs@LSP.LspFuncs{..} = do
131+ handleInit exitClientMsg clearReqId waitForCancel clientMsgChan lspFuncs@LSP.LspFuncs{..} = do
132+
88− ide <- getIdeState sendFunc (makeLSPVFSHandle lspFuncs)
133+ ide <- getIdeState getNextReqId sendFunc (makeLSPVFSHandle lspFuncs) clientCapabilities
134+
89 _ <- flip forkFinally (const exitClientMsg) $ forever $ do
135 _ <- flip forkFinally (const exitClientMsg) $ forever $ do
90 msg <- readChan clientMsgChan
136 msg <- readChan clientMsgChan
91 case msg of
137 case msg of
92− Notification NotificationMessage{_params} act -> act ide _params
138+ Notification x@NotificationMessage{_params} act -> do
139+ catch (act lspFuncs ide _params) $ \(e :: SomeException) ->
140+ logError (ideLogger ide) $ T.pack $
141+ "Unexpected exception on notification, please report!\n" ++
142+ "Message: " ++ show x ++ "\n" ++
143+ "Exception: " ++ show e
144+ 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)
93− Response RequestMessage{_id, _params} wrap act -> do
149+ ResponseAndRequest x@RequestMessage{_id, _params} wrap wrapNewReq act ->
94− res <- act ide _params
150+ checkCancelled ide clearReqId waitForCancel lspFuncs wrap act x _id _params $
95− sendFunc $ wrap $ ResponseMessage "2.0" (responseId _id) (Just res) Nothing
151+ \(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
156+ reqId <- getNextReqId
157+ 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
96 pure Nothing
164 pure Nothing
97
165
166+ checkCancelled ide clearReqId waitForCancel lspFuncs@LSP.LspFuncs{..} wrap act msg _id _params k =
167+ flip finally (clearReqId _id) $
168+ catch (do
169+ -- We could optimize this by first checking if the id
170+ -- is in the cancelled set. However, this is unlikely to be a
171+ -- bottleneck and the additional check might hide
172+ -- issues with async exceptions that need to be fixed.
173+ cancelOrRes <- race (waitForCancel _id) $ act lspFuncs ide _params
174+ case cancelOrRes of
175+ Left () -> do
176+ logDebug (ideLogger ide) $ T.pack $
177+ "Cancelled request " <> show _id
178+ sendFunc $ wrap $ ResponseMessage "2.0" (responseId _id) $ Left
179+ $ ResponseError RequestCancelled "" Nothing
180+ Right res -> k res
181+ ) $ \(e :: SomeException) -> do
182+ logError (ideLogger ide) $ T.pack $
183+ "Unexpected exception on request, please report!\n" ++
184+ "Message: " ++ show msg ++ "\n" ++
185+ "Exception: " ++ show e
186+ sendFunc $ wrap $ ResponseMessage "2.0" (responseId _id) $ Left
187+ $ ResponseError InternalError (T.pack $ show e) Nothing
188+
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)
98
200
99-- | Things that get sent to us, but we don't deal with.
201-- | Things that get sent to us, but we don't deal with.
100-- Set them to avoid a warning in VS Code output.
202-- Set them to avoid a warning in VS Code output.
101−setHandlersIgnore :: PartialHandlers
203+setHandlersIgnore :: PartialHandlers config
102setHandlersIgnore = PartialHandlers $ \_ x -> return x
204setHandlersIgnore = PartialHandlers $ \_ x -> return x
103− {LSP.cancelNotificationHandler = none
205+ {LSP.initializedHandler = none
104− ,LSP.initializedHandler = none
206+ ,LSP.responseHandler = none
105 }
207 }
106 where none = Just $ const $ return ()
208 where none = Just $ const $ return ()
107
209
210+cancelHandler :: (LspId -> IO ()) -> PartialHandlers config
211+cancelHandler cancelRequest = PartialHandlers $ \_ x -> return x
212+ {LSP.cancelNotificationHandler = Just $ \msg@NotificationMessage {_params = CancelParams {_id}} -> do
213+ cancelRequest _id
214+ whenJust (LSP.cancelNotificationHandler x) ($ msg)
215+ }
216+
108
217
109-- | A message that we need to deal with - the pieces are split up with existentials to gain additional type safety
218-- | A message that we need to deal with - the pieces are split up with existentials to gain additional type safety
110-- and defer precise processing until later (allows us to keep at a higher level of abstraction slightly longer)
219-- and defer precise processing until later (allows us to keep at a higher level of abstraction slightly longer)
111−data Message
220+data Message c
112− = forall m req resp . Response (RequestMessage m req resp) (ResponseMessage resp -> FromServerMessage) (IdeState -> req -> IO resp)
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))
222+ -- | Used for cases in which we need to send not only a response,
223+ -- but also an additional request to the client.
224+ -- 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)))
113− | forall m req . Notification (NotificationMessage m req) (IdeState -> req -> IO ())
226+ | forall m req . (Show m, Show req) => Notification (NotificationMessage m req) (LSP.LspFuncs c -> IdeState -> req -> IO ())
114−
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 ())
115
229
116modifyOptions :: LSP.Options -> LSP.Options
230modifyOptions :: LSP.Options -> LSP.Options
117−modifyOptions x = x{LSP.textDocumentSync = Just $ tweak orig}
231+modifyOptions x = x{ LSP.textDocumentSync = Just $ tweakTDS origTDS
232+ }
118 where
233 where
119− tweak x = x{_openClose=Just True, _change=Just TdSyncIncremental, _save=Just $ SaveOptions Nothing}
234+ tweakTDS tds = tds{_openClose=Just True, _change=Just TdSyncIncremental, _save=Just $ SaveOptions Nothing}
120− orig = fromMaybe tdsDefault $ LSP.textDocumentSync x
235+ origTDS = fromMaybe tdsDefault $ LSP.textDocumentSync x
121 tdsDefault = TextDocumentSyncOptions Nothing Nothing Nothing Nothing Nothing
236 tdsDefault = TextDocumentSyncOptions Nothing Nothing Nothing Nothing Nothing
src/Development/IDE/LSP/Notifications.hs+51−27
@@ -1,57 +1,81 @@
1−-- Copyright (c) 2019 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
1+-- Copyright (c) 2019 The DAML Authors. All rights reserved.
2-- SPDX-License-Identifier: Apache-2.0
2-- SPDX-License-Identifier: Apache-2.0
3
3
4{-# LANGUAGE DuplicateRecordFields #-}
4{-# LANGUAGE DuplicateRecordFields #-}
5−{-# LANGUAGE OverloadedStrings #-}
6−{-# LANGUAGE RankNTypes #-}
5+{-# LANGUAGE RankNTypes #-}
7
6
8module Development.IDE.LSP.Notifications
7module Development.IDE.LSP.Notifications
9 ( setHandlersNotifications
8 ( setHandlersNotifications
10 ) where
9 ) where
11
10
12−import Language.Haskell.LSP.Types
13import Development.IDE.LSP.Server
11import Development.IDE.LSP.Server
14−import qualified Language.Haskell.LSP.Core as LSP
12+import qualified Language.Haskell.LSP.Core as LSP
13+import Language.Haskell.LSP.Types
15−import qualified Language.Haskell.LSP.Types as LSP
14+import qualified Language.Haskell.LSP.Types as LSP
16
15
17−import Development.IDE.Types.Logger
16+import Development.IDE.Core.IdeConfiguration
18−import Development.IDE.Core.Service
17+import Development.IDE.Core.Service
19−import Development.IDE.Types.Location
18+import Development.IDE.Types.Location
19+import Development.IDE.Types.Logger
20
20
21+import Control.Monad.Extra
22+import Data.Foldable as F
23+import Data.Maybe
21−import qualified Data.Set as S
24+import qualified Data.HashSet as S
25+import qualified Data.Text as Text
22
26
23−import Development.IDE.Core.FileStore
27+import Development.IDE.Core.FileStore (setSomethingModified)
28+import Development.IDE.Core.FileExists (modifyFileExists)
24−import Development.IDE.Core.OfInterest
29+import Development.IDE.Core.OfInterest
25
30
26
31
27−whenUriFile :: IdeState -> Uri -> (NormalizedFilePath -> IO ()) -> IO ()
32+whenUriFile :: Uri -> (NormalizedFilePath -> IO ()) -> IO ()
28−whenUriFile ide uri act = case LSP.uriToFilePath uri of
33+whenUriFile uri act = whenJust (LSP.uriToFilePath uri) $ act . toNormalizedFilePath'
29− Just file -> act $ toNormalizedFilePath file
30− Nothing -> logWarning (ideLogger ide) $ "Unknown scheme in URI: " <> getUri uri
31
34
32−setHandlersNotifications :: PartialHandlers
35+setHandlersNotifications :: PartialHandlers c
33setHandlersNotifications = PartialHandlers $ \WithMessage{..} x -> return x
36setHandlersNotifications = PartialHandlers $ \WithMessage{..} x -> return x
34 {LSP.didOpenTextDocumentNotificationHandler = withNotification (LSP.didOpenTextDocumentNotificationHandler x) $
37 {LSP.didOpenTextDocumentNotificationHandler = withNotification (LSP.didOpenTextDocumentNotificationHandler x) $
35− \ide (DidOpenTextDocumentParams TextDocumentItem{_uri}) -> do
38+ \_ ide (DidOpenTextDocumentParams TextDocumentItem{_uri,_version}) -> do
36− setSomethingModified ide
39+ updatePositionMapping ide (VersionedTextDocumentIdentifier _uri (Just _version)) (List [])
37− whenUriFile ide _uri $ \file ->
40+ whenUriFile _uri $ \file -> do
38 modifyFilesOfInterest ide (S.insert file)
41 modifyFilesOfInterest ide (S.insert file)
39− logInfo (ideLogger ide) $ "Opened text document: " <> getUri _uri
42+ logInfo (ideLogger ide) $ "Opened text document: " <> getUri _uri
40
43
41 ,LSP.didChangeTextDocumentNotificationHandler = withNotification (LSP.didChangeTextDocumentNotificationHandler x) $
44 ,LSP.didChangeTextDocumentNotificationHandler = withNotification (LSP.didChangeTextDocumentNotificationHandler x) $
42− \ide (DidChangeTextDocumentParams VersionedTextDocumentIdentifier{_uri} _) -> do
45+ \_ ide (DidChangeTextDocumentParams identifier@VersionedTextDocumentIdentifier{_uri} changes) -> do
46+ updatePositionMapping ide identifier changes
43 setSomethingModified ide
47 setSomethingModified ide
44 logInfo (ideLogger ide) $ "Modified text document: " <> getUri _uri
48 logInfo (ideLogger ide) $ "Modified text document: " <> getUri _uri
45
49
46 ,LSP.didSaveTextDocumentNotificationHandler = withNotification (LSP.didSaveTextDocumentNotificationHandler x) $
50 ,LSP.didSaveTextDocumentNotificationHandler = withNotification (LSP.didSaveTextDocumentNotificationHandler x) $
47− \ide (DidSaveTextDocumentParams TextDocumentIdentifier{_uri}) -> do
51+ \_ ide (DidSaveTextDocumentParams TextDocumentIdentifier{_uri}) -> do
48 setSomethingModified ide
52 setSomethingModified ide
49 logInfo (ideLogger ide) $ "Saved text document: " <> getUri _uri
53 logInfo (ideLogger ide) $ "Saved text document: " <> getUri _uri
50
54
51 ,LSP.didCloseTextDocumentNotificationHandler = withNotification (LSP.didCloseTextDocumentNotificationHandler x) $
55 ,LSP.didCloseTextDocumentNotificationHandler = withNotification (LSP.didCloseTextDocumentNotificationHandler x) $
52− \ide (DidCloseTextDocumentParams TextDocumentIdentifier{_uri}) -> do
56+ \_ ide (DidCloseTextDocumentParams TextDocumentIdentifier{_uri}) -> do
53− setSomethingModified ide
54− whenUriFile ide _uri $ \file ->
57+ whenUriFile _uri $ \file -> do
55 modifyFilesOfInterest ide (S.delete file)
58 modifyFilesOfInterest ide (S.delete file)
56− logInfo (ideLogger ide) $ "Closed text document: " <> getUri _uri
59+ logInfo (ideLogger ide) $ "Closed text document: " <> getUri _uri
60+ ,LSP.didChangeWatchedFilesNotificationHandler = withNotification (LSP.didChangeWatchedFilesNotificationHandler x) $
61+ \_ ide (DidChangeWatchedFilesParams fileEvents) -> do
62+ let events =
63+ mapMaybe
64+ (\(FileEvent uri ev) ->
65+ (, ev /= FcDeleted) . toNormalizedFilePath'
66+ <$> LSP.uriToFilePath uri
67+ )
68+ ( F.toList fileEvents )
69+ let msg = Text.pack $ show events
70+ logInfo (ideLogger ide) $ "Files created or deleted: " <> msg
71+ modifyFileExists ide events
72+ 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))
57 }
81 }
src/Development/IDE/LSP/Outline.hsnew file+234
@@ -0,0 +1,234 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE DuplicateRecordFields #-}
3+#include "ghc-api-version.h"
4+
5+module Development.IDE.LSP.Outline
6+ ( setHandlersOutline
7+ -- * For haskell-language-server
8+ , moduleOutline
9+ )
10+where
11+
12+import qualified Language.Haskell.LSP.Core as LSP
13+import Language.Haskell.LSP.Messages
14+import Language.Haskell.LSP.Types
15+import Data.Functor
16+import Data.Generics
17+import Data.Maybe
18+import Data.Text ( Text
19+ , pack
20+ )
21+import qualified Data.Text as T
22+import Development.IDE.Core.Rules
23+import Development.IDE.Core.Shake
24+import Development.IDE.GHC.Compat
25+import Development.IDE.GHC.Error ( srcSpanToRange )
26+import Development.IDE.LSP.Server
27+import Development.IDE.Types.Location
28+import Outputable ( Outputable
29+ , ppr
30+ , showSDocUnsafe
31+ )
32+
33+setHandlersOutline :: PartialHandlers c
34+setHandlersOutline = PartialHandlers $ \WithMessage {..} x -> return x
35+ { LSP.documentSymbolHandler = withResponse RspDocumentSymbols moduleOutline
36+ }
37+
38+moduleOutline
39+ :: LSP.LspFuncs c -> IdeState -> DocumentSymbolParams -> IO (Either ResponseError DSResult)
40+moduleOutline _lsp ideState DocumentSymbolParams { _textDocument = TextDocumentIdentifier uri }
41+ = case uriToFilePath uri of
42+ Just (toNormalizedFilePath' -> fp) -> do
43+ mb_decls <- runAction ideState $ use GetParsedModule fp
44+ pure $ Right $ case mb_decls of
45+ Nothing -> DSDocumentSymbols (List [])
46+ Just ParsedModule { pm_parsed_source = L _ltop HsModule { hsmodName, hsmodDecls, hsmodImports } }
47+ -> let
48+ declSymbols = mapMaybe documentSymbolForDecl hsmodDecls
49+ moduleSymbol = hsmodName <&> \(L l m) ->
50+ (defDocumentSymbol l :: DocumentSymbol)
51+ { _name = pprText m
52+ , _kind = SkFile
53+ , _range = Range (Position 0 0) (Position maxBound 0) -- _ltop is 0 0 0 0
54+ }
55+ importSymbols = maybe [] pure $
56+ documentSymbolForImportSummary
57+ (mapMaybe documentSymbolForImport hsmodImports)
58+ allSymbols = case moduleSymbol of
59+ Nothing -> importSymbols <> declSymbols
60+ Just x ->
61+ [ x { _children = Just (List (importSymbols <> declSymbols))
62+ }
63+ ]
64+ in
65+ DSDocumentSymbols (List allSymbols)
66+
67+
68+ Nothing -> pure $ Right $ DSDocumentSymbols (List [])
69+
70+documentSymbolForDecl :: Located (HsDecl GhcPs) -> Maybe DocumentSymbol
71+documentSymbolForDecl (L l (TyClD FamDecl { tcdFam = FamilyDecl { fdLName = L _ n, fdInfo, fdTyVars } }))
72+ = Just (defDocumentSymbol l :: DocumentSymbol)
73+ { _name = showRdrName n
74+ <> (case pprText fdTyVars of
75+ "" -> ""
76+ t -> " " <> t
77+ )
78+ , _detail = Just $ pprText fdInfo
79+ , _kind = SkClass
80+ }
81+documentSymbolForDecl (L l (TyClD ClassDecl { tcdLName = L _ name, tcdSigs, tcdTyVars }))
82+ = Just (defDocumentSymbol l :: DocumentSymbol)
83+ { _name = showRdrName name
84+ <> (case pprText tcdTyVars of
85+ "" -> ""
86+ t -> " " <> t
87+ )
88+ , _kind = SkClass
89+ , _detail = Just "class"
90+ , _children =
91+ Just $ List
92+ [ (defDocumentSymbol l :: DocumentSymbol)
93+ { _name = showRdrName n
94+ , _kind = SkMethod
95+ , _selectionRange = srcSpanToRange l'
96+ }
97+ | L l (ClassOpSig False names _) <- tcdSigs
98+ , L l' n <- names
99+ ]
100+ }
101+documentSymbolForDecl (L l (TyClD DataDecl { tcdLName = L _ name, tcdDataDefn = HsDataDefn { dd_cons } }))
102+ = Just (defDocumentSymbol l :: DocumentSymbol)
103+ { _name = showRdrName name
104+ , _kind = SkStruct
105+ , _children =
106+ Just $ List
107+ [ (defDocumentSymbol l :: DocumentSymbol)
108+ { _name = showRdrName n
109+ , _kind = SkConstructor
110+ , _selectionRange = srcSpanToRange l'
111+ , _children = conArgRecordFields (getConArgs x)
112+ }
113+ | L l x <- dd_cons
114+ , L l' n <- getConNames x
115+ ]
116+ }
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
128+documentSymbolForDecl (L l (TyClD SynDecl { tcdLName = L l' n })) = Just
129+ (defDocumentSymbol l :: DocumentSymbol) { _name = showRdrName n
130+ , _kind = SkTypeParameter
131+ , _selectionRange = srcSpanToRange l'
132+ }
133+documentSymbolForDecl (L l (InstD ClsInstD { cid_inst = ClsInstDecl { cid_poly_ty } }))
134+ = Just (defDocumentSymbol l :: DocumentSymbol) { _name = pprText cid_poly_ty
135+ , _kind = SkInterface
136+ }
137+documentSymbolForDecl (L l (InstD DataFamInstD { dfid_inst = DataFamInstDecl HsIB { hsib_body = FamEqn { feqn_tycon, feqn_pats } } }))
138+ = Just (defDocumentSymbol l :: DocumentSymbol)
139+ { _name = showRdrName (unLoc feqn_tycon) <> " " <> T.unwords
140+ (map pprText feqn_pats)
141+ , _kind = SkInterface
142+ }
143+documentSymbolForDecl (L l (InstD TyFamInstD { tfid_inst = TyFamInstDecl HsIB { hsib_body = FamEqn { feqn_tycon, feqn_pats } } }))
144+ = Just (defDocumentSymbol l :: DocumentSymbol)
145+ { _name = showRdrName (unLoc feqn_tycon) <> " " <> T.unwords
146+ (map pprText feqn_pats)
147+ , _kind = SkInterface
148+ }
149+documentSymbolForDecl (L l (DerivD DerivDecl { deriv_type })) =
150+ gfindtype deriv_type <&> \(L (_ :: SrcSpan) name) ->
151+ (defDocumentSymbol l :: DocumentSymbol) { _name = pprText @(HsType GhcPs)
152+ name
153+ , _kind = SkInterface
154+ }
155+documentSymbolForDecl (L l (ValD FunBind{fun_id = L _ name})) = Just
156+ (defDocumentSymbol l :: DocumentSymbol)
157+ { _name = showRdrName name
158+ , _kind = SkFunction
159+ }
160+documentSymbolForDecl (L l (ValD PatBind{pat_lhs})) = Just
161+ (defDocumentSymbol l :: DocumentSymbol)
162+ { _name = pprText pat_lhs
163+ , _kind = SkFunction
164+ }
165+
166+documentSymbolForDecl (L l (ForD x)) = Just
167+ (defDocumentSymbol l :: DocumentSymbol)
168+ { _name = case x of
169+ ForeignImport{} -> name
170+ ForeignExport{} -> name
171+#if MIN_GHC_API_VERSION(8,6,0)
172+ XForeignDecl{} -> "?"
173+#endif
174+ , _kind = SkObject
175+ , _detail = case x of
176+ ForeignImport{} -> Just "import"
177+ ForeignExport{} -> Just "export"
178+#if MIN_GHC_API_VERSION(8,6,0)
179+ XForeignDecl{} -> Nothing
180+#endif
181+ }
182+ where name = showRdrName $ unLoc $ fd_name x
183+
184+documentSymbolForDecl _ = Nothing
185+
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+
205+documentSymbolForImport :: Located (ImportDecl GhcPs) -> Maybe DocumentSymbol
206+documentSymbolForImport (L l ImportDecl { ideclName, ideclQualified }) = Just
207+ (defDocumentSymbol l :: DocumentSymbol)
208+ { _name = "import " <> pprText ideclName
209+ , _kind = SkModule
210+#if MIN_GHC_API_VERSION(8,10,0)
211+ , _detail = case ideclQualified of { NotQualified -> Nothing; _ -> Just "qualified" }
212+#else
213+ , _detail = if ideclQualified then Just "qualified" else Nothing
214+#endif
215+ }
216+#if MIN_GHC_API_VERSION(8,6,0)
217+documentSymbolForImport (L _ XImportDecl {}) = Nothing
218+#endif
219+
220+defDocumentSymbol :: SrcSpan -> DocumentSymbol
221+defDocumentSymbol l = DocumentSymbol { .. } where
222+ _detail = Nothing
223+ _deprecated = Nothing
224+ _name = ""
225+ _kind = SkUnknown 0
226+ _range = srcSpanToRange l
227+ _selectionRange = srcSpanToRange l
228+ _children = Nothing
229+
230+showRdrName :: RdrName -> Text
231+showRdrName = pprText
232+
233+pprText :: Outputable a => a -> Text
234+pprText = pack . showSDocUnsafe . ppr
src/Development/IDE/LSP/Protocol.hs+1−1
@@ -1,4 +1,4 @@
1−-- Copyright (c) 2019 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
1+-- Copyright (c) 2019 The DAML Authors. All rights reserved.
2-- SPDX-License-Identifier: Apache-2.0
2-- SPDX-License-Identifier: Apache-2.0
3{-# LANGUAGE PatternSynonyms #-}
3{-# LANGUAGE PatternSynonyms #-}
4
4
src/Development/IDE/LSP/Server.hs+18−11
@@ -1,4 +1,4 @@
1−-- Copyright (c) 2019 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
1+-- Copyright (c) 2019 The DAML Authors. All rights reserved.
2-- SPDX-License-Identifier: Apache-2.0
2-- SPDX-License-Identifier: Apache-2.0
3
3
4{-# LANGUAGE DuplicateRecordFields #-}
4{-# LANGUAGE DuplicateRecordFields #-}
@@ -16,25 +16,32 @@ import qualified Language.Haskell.LSP.Core as LSP
16import qualified Language.Haskell.LSP.Messages as LSP
16import qualified Language.Haskell.LSP.Messages as LSP
17import Development.IDE.Core.Service
17import Development.IDE.Core.Service
18
18
19−
20−data WithMessage = WithMessage
19+data WithMessage c = WithMessage
21− {withResponse :: forall m req resp .
20+ {withResponse :: forall m req resp . (Show m, Show req) =>
22 (ResponseMessage resp -> LSP.FromServerMessage) -> -- how to wrap a response
21 (ResponseMessage resp -> LSP.FromServerMessage) -> -- how to wrap a response
23− (IdeState -> req -> IO resp) -> -- actual work
22+ (LSP.LspFuncs c -> IdeState -> req -> IO (Either ResponseError resp)) -> -- actual work
24 Maybe (LSP.Handler (RequestMessage m req resp))
23 Maybe (LSP.Handler (RequestMessage m req resp))
25− ,withNotification :: forall m req .
24+ ,withNotification :: forall m req . (Show m, Show req) =>
26 Maybe (LSP.Handler (NotificationMessage m req)) -> -- old notification handler
25 Maybe (LSP.Handler (NotificationMessage m req)) -> -- old notification handler
27− (IdeState -> req -> IO ()) -> -- actual work
26+ (LSP.LspFuncs c -> IdeState -> req -> IO ()) -> -- actual work
28 Maybe (LSP.Handler (NotificationMessage m req))
27 Maybe (LSP.Handler (NotificationMessage m req))
28+ ,withResponseAndRequest :: forall m rm req resp newReqParams newReqBody .
29+ (Show m, Show rm, Show req, Show newReqParams, Show newReqBody) =>
30+ (ResponseMessage resp -> LSP.FromServerMessage) -> -- how to wrap a response
31+ (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+ Maybe (LSP.Handler (RequestMessage m req resp))
34+ , withInitialize :: (LSP.LspFuncs c -> IdeState -> InitializeParams -> IO ())
35+ -> Maybe (LSP.Handler InitializeRequest)
29 }
36 }
30
37
31−newtype PartialHandlers = PartialHandlers (WithMessage -> LSP.Handlers -> IO LSP.Handlers)
38+newtype PartialHandlers c = PartialHandlers (WithMessage c -> LSP.Handlers -> IO LSP.Handlers)
32
39
33−instance Default PartialHandlers where
40+instance Default (PartialHandlers c) where
34 def = PartialHandlers $ \_ x -> pure x
41 def = PartialHandlers $ \_ x -> pure x
35
42
36−instance Semigroup PartialHandlers where
43+instance Semigroup (PartialHandlers c) where
37 PartialHandlers a <> PartialHandlers b = PartialHandlers $ \w x -> a w x >>= b w
44 PartialHandlers a <> PartialHandlers b = PartialHandlers $ \w x -> a w x >>= b w
38
45
39−instance Monoid PartialHandlers where
46+instance Monoid (PartialHandlers c) where
40 mempty = def
47 mempty = def
src/Development/IDE/Plugin.hsnew file+60
@@ -0,0 +1,60 @@
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.hsnew file+570
@@ -0,0 +1,570 @@
1+-- Copyright (c) 2019 The DAML Authors. All rights reserved.
2+-- SPDX-License-Identifier: Apache-2.0
3+
4+{-# LANGUAGE DuplicateRecordFields #-}
5+{-# LANGUAGE CPP #-}
6+#include "ghc-api-version.h"
7+
8+-- | 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
18+ ) where
19+
20+import Language.Haskell.LSP.Types
21+import Control.Monad (join)
22+import Development.IDE.Plugin
23+import Development.IDE.GHC.Compat
24+import Development.IDE.Core.Rules
25+import Development.IDE.Core.RuleTypes
26+import Development.IDE.Core.Service
27+import Development.IDE.Core.Shake
28+import Development.IDE.GHC.Error
29+import Development.IDE.GHC.Util
30+import 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
34+import Development.IDE.Types.Location
35+import Development.IDE.Types.Options
36+import Development.Shake (Rules)
37+import qualified Data.HashMap.Strict as Map
38+import qualified Language.Haskell.LSP.Core as LSP
39+import Language.Haskell.LSP.VFS
40+import Language.Haskell.LSP.Messages
41+import qualified Data.Rope.UTF16 as Rope
42+import Data.Aeson.Types (toJSON, fromJSON, Value(..), Result(..))
43+import Control.Monad.Trans.Maybe
44+import Data.Char
45+import Data.Maybe
46+import Data.List.Extra
47+import qualified Data.Text as T
48+import Data.Tuple.Extra ((&&&))
49+import HscTypes
50+import Parser
51+import Text.Regex.TDFA ((=~), (=~~))
52+import Text.Regex.TDFA.Text()
53+import 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
62+
63+-- | Generate code actions.
64+codeAction
65+ :: LSP.LspFuncs c
66+ -> IdeState
67+ -> TextDocumentIdentifier
68+ -> Range
69+ -> CodeActionContext
70+ -> IO (Either ResponseError [CAResult])
71+codeAction lsp state (TextDocumentIdentifier uri) _range CodeActionContext{_diagnostics=List xs} = do
72+ -- disable logging as its quite verbose
73+ -- logInfo (ideLogger ide) $ T.pack $ "Code action req: " ++ show arg
74+ contents <- LSP.getVirtualFileFunc lsp $ toNormalizedUri uri
75+ 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
84+ [ 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
86+ , let edit = WorkspaceEdit (Just $ Map.singleton uri $ List tedit) Nothing
87+ ]
88+
89+-- | Generate code lenses.
90+codeLens
91+ :: LSP.LspFuncs c
92+ -> IdeState
93+ -> CodeLensParams
94+ -> IO (Either ResponseError (List CodeLens))
95+codeLens _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
99+ _ <- runAction ideState $ runMaybeT $ useE TypeCheck filePath
100+ diag <- getDiagnostics ideState
101+ 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
105+ , dFile == filePath
106+ , (title, tedit) <- suggestSignature False dDiag
107+ , let edit = WorkspaceEdit (Just $ Map.singleton uri $ List tedit) Nothing
108+ ]
109+ Nothing -> pure []
110+
111+-- | Execute the "typesignature.add" command.
112+executeAddSignatureCommand
113+ :: LSP.LspFuncs c
114+ -> IdeState
115+ -> ExecuteCommandParams
116+ -> IO (Either ResponseError Value, Maybe (ServerMethod, ApplyWorkspaceEditParams))
117+executeAddSignatureCommand _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
123+ , Just (List [edit]) <- _arguments
124+ , Success wedit <- fromJSON edit
125+ = return (Right Null, Just (WorkspaceApplyEdit, ApplyWorkspaceEditParams wedit))
126+ | 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
138+ [ suggestAddExtension diag
139+ , suggestExtendImport dflags text diag
140+ , suggestFillHole diag
141+ , suggestFillTypeWildcard diag
142+ , suggestFixConstructorImport text diag
143+ , suggestModuleTypo diag
144+ , suggestReplaceIdentifier text diag
145+ , suggestSignature True diag
146+ ] ++ concat
147+ [ suggestNewDefinition ideOptions pm text diag
148+ ++ suggestRemoveRedundantImport pm text diag
149+ ++ suggestNewImport packageExports pm diag
150+ | Just pm <- [parsedModule]]
151+
152+
153+suggestRemoveRedundantImport :: ParsedModule -> Maybe T.Text -> Diagnostic -> [(T.Text, [TextEdit])]
154+suggestRemoveRedundantImport ParsedModule{pm_parsed_source = L _ HsModule{hsmodImports}} contents Diagnostic{_range=_range,..}
155+-- The qualified import of ‘many’ from module ‘Control.Applicative’ is redundant
156+ | Just [_, bindings] <- matchRegex _message "The( qualified)? import of ‘([^’]*)’ from module [^ ]* is redundant"
157+ , Just (L _ impDecl) <- find (\(L l _) -> srcSpanToRange l == _range ) hsmodImports
158+ , Just c <- contents
159+ , ranges <- map (rangesForBinding impDecl . T.unpack) (T.splitOn ", " bindings)
160+ , ranges' <- extendAllToIncludeCommaIfPossible (indexedByPosition $ T.unpack c) (concat ranges)
161+ , not (null ranges')
162+ = [( "Remove " <> bindings <> " from import" , [ TextEdit r "" | r <- ranges' ] )]
163+
164+-- File.hs:16:1: warning:
165+-- The import of `Data.List' is redundant
166+-- except perhaps to import instances from `Data.List'
167+-- To import instances alone, use: import Data.List()
168+ | _message =~ ("The( qualified)? import of [^ ]* is redundant" :: String)
169+ = [("Remove import", [TextEdit (extendToWholeLineIfPossible contents _range) ""])]
170+ | otherwise = []
171+
172+suggestReplaceIdentifier :: Maybe T.Text -> Diagnostic -> [(T.Text, [TextEdit])]
173+suggestReplaceIdentifier contents Diagnostic{_range=_range,..}
174+-- File.hs:52:41: error:
175+-- * Variable not in scope:
176+-- suggestAcion :: Maybe T.Text -> Range -> Range
177+-- * Perhaps you meant ‘suggestAction’ (line 83)
178+-- File.hs:94:37: error:
179+-- Not in scope: ‘T.isPrfixOf’
180+-- Perhaps you meant one of these:
181+-- ‘T.isPrefixOf’ (imported from Data.Text),
182+-- ‘T.isInfixOf’ (imported from Data.Text),
183+-- ‘T.isSuffixOf’ (imported from Data.Text)
184+-- Module ‘Data.Text’ does not export ‘isPrfixOf’.
185+ | renameSuggestions@(_:_) <- extractRenamableTerms _message
186+ = [ ("Replace with ‘" <> name <> "’", [mkRenameEdit contents _range name]) | name <- renameSuggestions ]
187+ | otherwise = []
188+
189+suggestNewDefinition :: IdeOptions -> ParsedModule -> Maybe T.Text -> Diagnostic -> [(T.Text, [TextEdit])]
190+suggestNewDefinition ideOptions parsedModule contents Diagnostic{_message, _range}
191+-- * Variable not in scope:
192+-- suggestAcion :: Maybe T.Text -> Range -> Range
193+ | Just [name, typ] <- matchRegex message "Variable not in scope: ([^ ]+) :: ([^*•]+)"
194+ = newDefinitionAction ideOptions parsedModule _range name typ
195+ | Just [name, typ] <- matchRegex message "Found hole: _([^ ]+) :: ([^*•]+) Or perhaps"
196+ , [(label, newDefinitionEdits)] <- newDefinitionAction ideOptions parsedModule _range name typ
197+ = [(label, mkRenameEdit contents _range name : newDefinitionEdits)]
198+ | otherwise = []
199+ where
200+ message = unifySpaces _message
201+
202+newDefinitionAction :: IdeOptions -> ParsedModule -> Range -> T.Text -> T.Text -> [(T.Text, [TextEdit])]
203+newDefinitionAction IdeOptions{..} parsedModule Range{_start} name typ
204+ | Range _ lastLineP : _ <-
205+ [ srcSpanToRange l
206+ | (L l _) <- hsmodDecls
207+ , _start `isInsideSrcSpan` l]
208+ , nextLineP <- Position{ _line = _line lastLineP + 1, _character = 0}
209+ = [ ("Define " <> sig
210+ , [TextEdit (Range nextLineP nextLineP) (T.unlines ["", sig, name <> " = error \"not implemented\""])]
211+ )]
212+ | otherwise = []
213+ where
214+ colon = if optNewColonConvention then " : " else " :: "
215+ sig = name <> colon <> T.dropWhileEnd isSpace typ
216+ ParsedModule{pm_parsed_source = L _ HsModule{hsmodDecls}} = parsedModule
217+
218+
219+suggestFillTypeWildcard :: Diagnostic -> [(T.Text, [TextEdit])]
220+suggestFillTypeWildcard Diagnostic{_range=_range,..}
221+-- Foo.hs:3:8: error:
222+-- * Found type wildcard `_' standing for `p -> p1 -> p'
223+
224+ | "Found type wildcard" `T.isInfixOf` _message
225+ , " standing for " `T.isInfixOf` _message
226+ , typeSignature <- extractWildCardTypeSignature _message
227+ = [("Use type signature: ‘" <> typeSignature <> "’", [TextEdit _range typeSignature])]
228+ | otherwise = []
229+
230+suggestAddExtension :: Diagnostic -> [(T.Text, [TextEdit])]
231+suggestAddExtension Diagnostic{_range=_range,..}
232+-- File.hs:22:8: error:
233+-- Illegal lambda-case (use -XLambdaCase)
234+-- File.hs:22:6: error:
235+-- Illegal view pattern: x -> foo
236+-- Use ViewPatterns to enable view patterns
237+-- File.hs:26:8: error:
238+-- Illegal `..' in record pattern
239+-- Use RecordWildCards to permit this
240+-- File.hs:53:28: error:
241+-- Illegal tuple section: use TupleSections
242+-- File.hs:238:29: error:
243+-- * Can't make a derived instance of `Data FSATrace':
244+-- You need DeriveDataTypeable to derive an instance for this class
245+-- * In the data declaration for `FSATrace'
246+-- C:\Neil\shake\src\Development\Shake\Command.hs:515:31: error:
247+-- * Illegal equational constraint a ~ ()
248+-- (Use GADTs or TypeFamilies to permit this)
249+-- * In the context: a ~ ()
250+-- While checking an instance declaration
251+-- In the instance declaration for `Unit (m a)'
252+ | exts@(_:_) <- filter (`Map.member` ghcExtensions) $ T.split (not . isAlpha) $ T.replace "-X" "" _message
253+ = [("Add " <> x <> " extension", [TextEdit (Range (Position 0 0) (Position 0 0)) $ "{-# LANGUAGE " <> x <> " #-}\n"]) | x <- exts]
254+ | otherwise = []
255+
256+-- | All the GHC extensions
257+ghcExtensions :: Map.HashMap T.Text Extension
258+ghcExtensions = Map.fromList . map ( ( T.pack . flagSpecName ) &&& flagSpecFlag ) $ xFlags
259+
260+suggestModuleTypo :: Diagnostic -> [(T.Text, [TextEdit])]
261+suggestModuleTypo Diagnostic{_range=_range,..}
262+-- src/Development/IDE/Core/Compile.hs:58:1: error:
263+-- Could not find module ‘Data.Cha’
264+-- Perhaps you meant Data.Char (from base-4.12.0.0)
265+ | "Could not find module" `T.isInfixOf` _message
266+ , "Perhaps you meant" `T.isInfixOf` _message = let
267+ findSuggestedModules = map (head . T.words) . drop 2 . T.lines
268+ proposeModule mod = ("replace with " <> mod, [TextEdit _range mod])
269+ in map proposeModule $ nubOrd $ findSuggestedModules _message
270+ | otherwise = []
271+
272+suggestFillHole :: Diagnostic -> [(T.Text, [TextEdit])]
273+suggestFillHole Diagnostic{_range=_range,..}
274+-- ...Development/IDE/LSP/CodeAction.hs:103:9: warning:
275+-- * Found hole: _ :: Int -> String
276+-- * In the expression: _
277+-- In the expression: _ a
278+-- In an equation for ‘foo’: foo a = _ a
279+-- * Relevant bindings include
280+-- a :: Int
281+-- (bound at ...Development/IDE/LSP/CodeAction.hs:103:5)
282+-- foo :: Int -> String
283+-- (bound at ...Development/IDE/LSP/CodeAction.hs:103:1)
284+-- Valid hole fits include
285+-- foo :: Int -> String
286+-- (bound at ...Development/IDE/LSP/CodeAction.hs:103:1)
287+-- show :: forall a. Show a => a -> String
288+-- with show @Int
289+-- (imported from ‘Prelude’ at ...Development/IDE/LSP/CodeAction.hs:7:8-37
290+-- (and originally defined in ‘GHC.Show’))
291+-- mempty :: forall a. Monoid a => a
292+-- with mempty @(Int -> String)
293+-- (imported from ‘Prelude’ at ...Development/IDE/LSP/CodeAction.hs:7:8-37
294+-- (and originally defined in ‘GHC.Base’)) (lsp-ui)
295+
296+ | topOfHoleFitsMarker `T.isInfixOf` _message = let
297+ findSuggestedHoleFits :: T.Text -> [T.Text]
298+ findSuggestedHoleFits = extractFitNames . selectLinesWithFits . dropPreceding . T.lines
299+ proposeHoleFit name = ("replace hole `" <> holeName <> "` with " <> name, [TextEdit _range name])
300+ holeName = T.strip $ last $ T.splitOn ":" $ head . T.splitOn "::" $ head $ filter ("Found hole" `T.isInfixOf`) $ T.lines _message
301+ dropPreceding = dropWhile (not . (topOfHoleFitsMarker `T.isInfixOf`))
302+ selectLinesWithFits = filter ("::" `T.isInfixOf`)
303+ extractFitNames = map (T.strip . head . T.splitOn " :: ")
304+ in map proposeHoleFit $ nubOrd $ findSuggestedHoleFits _message
305+
306+ | otherwise = []
307+
308+suggestExtendImport :: Maybe DynFlags -> Maybe T.Text -> Diagnostic -> [(T.Text, [TextEdit])]
309+suggestExtendImport (Just dflags) contents Diagnostic{_range=_range,..}
310+ | Just [binding, mod, srcspan] <-
311+ matchRegex _message
312+ "Perhaps you want to add ‘([^’]*)’ to the import list in the import of ‘([^’]*)’ *\\((.*)\\).$"
313+ , Just c <- contents
314+ , POk _ (L _ name) <- runParser dflags (T.unpack binding) parseIdentifier
315+ = let range = case [ x | (x,"") <- readSrcSpan (T.unpack srcspan)] of
316+ [s] -> let x = srcSpanToRange s
317+ in x{_end = (_end x){_character = succ (_character (_end x))}}
318+ _ -> error "bug in srcspan parser"
319+ importLine = textInRange range c
320+ in [("Add " <> binding <> " to the import list of " <> mod
321+ , [TextEdit range (addBindingToImportList (T.pack $ printRdrName name) importLine)])]
322+ | otherwise = []
323+suggestExtendImport Nothing _ _ = []
324+
325+suggestFixConstructorImport :: Maybe T.Text -> Diagnostic -> [(T.Text, [TextEdit])]
326+suggestFixConstructorImport _ Diagnostic{_range=_range,..}
327+ -- ‘Success’ is a data constructor of ‘Result’
328+ -- To import it use
329+ -- import Data.Aeson.Types( Result( Success ) )
330+ -- or
331+ -- import Data.Aeson.Types( Result(..) ) (lsp-ui)
332+ | Just [constructor, typ] <-
333+ matchRegex _message
334+ "‘([^’]*)’ is a data constructor of ‘([^’]*)’ To import it use"
335+ = let fixedImport = typ <> "(" <> constructor <> ")"
336+ in [("Fix import of " <> fixedImport, [TextEdit _range fixedImport])]
337+ | otherwise = []
338+
339+suggestSignature :: Bool -> Diagnostic -> [(T.Text, [TextEdit])]
340+suggestSignature isQuickFix Diagnostic{_range=_range@Range{..},..}
341+ | _message =~
342+ ("(Top-level binding|Polymorphic local binding|Pattern synonym) with no type signature" :: T.Text) = let
343+ signature = removeInitialForAll
344+ $ T.takeWhile (\x -> x/='*' && x/='•')
345+ $ T.strip $ unifySpaces $ last $ T.splitOn "type signature: " $ filterNewlines _message
346+ startOfLine = Position (_line _start) startCharacter
347+ beforeLine = Range startOfLine startOfLine
348+ title = if isQuickFix then "add signature: " <> signature else signature
349+ action = TextEdit beforeLine $ signature <> "\n" <> T.replicate startCharacter " "
350+ in [(title, [action])]
351+ where removeInitialForAll :: T.Text -> T.Text
352+ removeInitialForAll (T.breakOnEnd " :: " -> (nm, ty))
353+ | "forall" `T.isPrefixOf` ty = nm <> T.drop 2 (snd (T.breakOn "." ty))
354+ | otherwise = nm <> ty
355+ startCharacter
356+ | "Polymorphic local binding" `T.isPrefixOf` _message
357+ = _character _start
358+ | otherwise
359+ = 0
360+
361+suggestSignature _ _ = []
362+
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+
438+topOfHoleFitsMarker :: T.Text
439+topOfHoleFitsMarker =
440+#if MIN_GHC_API_VERSION(8,6,0)
441+ "Valid hole fits include"
442+#else
443+ "Valid substitutions include"
444+#endif
445+
446+mkRenameEdit :: Maybe T.Text -> Range -> T.Text -> TextEdit
447+mkRenameEdit contents range name =
448+ if maybeIsInfixFunction == Just True
449+ then TextEdit range ("`" <> name <> "`")
450+ else TextEdit range name
451+ where
452+ maybeIsInfixFunction = do
453+ curr <- textInRange range <$> contents
454+ pure $ "`" `T.isPrefixOf` curr && "`" `T.isSuffixOf` curr
455+
456+extractWildCardTypeSignature :: T.Text -> T.Text
457+extractWildCardTypeSignature =
458+ -- inferring when parens are actually needed around the type signature would
459+ -- require understanding both the precedence of the context of the _ and of
460+ -- the signature itself. Inserting them unconditionally is ugly but safe.
461+ ("(" `T.append`) . (`T.append` ")") .
462+ T.takeWhile (/='’') . T.dropWhile (=='‘') . T.dropWhile (/='‘') .
463+ snd . T.breakOnEnd "standing for "
464+
465+extractRenamableTerms :: T.Text -> [T.Text]
466+extractRenamableTerms msg
467+ -- Account for both "Variable not in scope" and "Not in scope"
468+ | "ot in scope:" `T.isInfixOf` msg = extractSuggestions msg
469+ | otherwise = []
470+ where
471+ extractSuggestions = map getEnclosed
472+ . concatMap singleSuggestions
473+ . filter isKnownSymbol
474+ . T.lines
475+ singleSuggestions = T.splitOn "), " -- Each suggestion is comma delimited
476+ isKnownSymbol t = " (imported from" `T.isInfixOf` t || " (line " `T.isInfixOf` t
477+ getEnclosed = T.dropWhile (== '‘')
478+ . T.dropWhileEnd (== '’')
479+ . T.dropAround (\c -> c /= '‘' && c /= '’')
480+
481+-- | If a range takes up a whole line (it begins at the start of the line and there's only whitespace
482+-- between the end of the range and the next newline), extend the range to take up the whole line.
483+extendToWholeLineIfPossible :: Maybe T.Text -> Range -> Range
484+extendToWholeLineIfPossible contents range@Range{..} =
485+ let newlineAfter = maybe False (T.isPrefixOf "\n" . T.dropWhile (\x -> isSpace x && x /= '\n') . snd . splitTextAtPosition _end) contents
486+ extend = newlineAfter && _character _start == 0 -- takes up an entire line, so remove the whole line
487+ in if extend then Range _start (Position (_line _end + 1) 0) else range
488+
489+splitTextAtPosition :: Position -> T.Text -> (T.Text, T.Text)
490+splitTextAtPosition (Position row col) x
491+ | (preRow, mid:postRow) <- splitAt row $ T.splitOn "\n" x
492+ , (preCol, postCol) <- T.splitAt col mid
493+ = (T.intercalate "\n" $ preRow ++ [preCol], T.intercalate "\n" $ postCol : postRow)
494+ | otherwise = (x, T.empty)
495+
496+-- | Returns [start .. end[
497+textInRange :: Range -> T.Text -> T.Text
498+textInRange (Range (Position startRow startCol) (Position endRow endCol)) text =
499+ case compare startRow endRow of
500+ LT ->
501+ let (linesInRangeBeforeEndLine, endLineAndFurtherLines) = splitAt (endRow - startRow) linesBeginningWithStartLine
502+ (textInRangeInFirstLine, linesBetween) = case linesInRangeBeforeEndLine of
503+ [] -> ("", [])
504+ firstLine:linesInBetween -> (T.drop startCol firstLine, linesInBetween)
505+ maybeTextInRangeInEndLine = T.take endCol <$> listToMaybe endLineAndFurtherLines
506+ in T.intercalate "\n" (textInRangeInFirstLine : linesBetween ++ maybeToList maybeTextInRangeInEndLine)
507+ EQ ->
508+ let line = fromMaybe "" (listToMaybe linesBeginningWithStartLine)
509+ in T.take (endCol - startCol) (T.drop startCol line)
510+ GT -> ""
511+ where
512+ linesBeginningWithStartLine = drop startRow (T.splitOn "\n" text)
513+
514+-- | Returns the ranges for a binding in an import declaration
515+rangesForBinding :: ImportDecl GhcPs -> String -> [Range]
516+rangesForBinding ImportDecl{ideclHiding = Just (False, L _ lies)} b =
517+ concatMap (map srcSpanToRange . rangesForBinding' b') lies
518+ where
519+ b' = wrapOperatorInParens (unqualify b)
520+
521+ wrapOperatorInParens x = if isAlpha (head x) then x else "(" <> x <> ")"
522+
523+ unqualify x = snd $ breakOnEnd "." x
524+
525+rangesForBinding _ _ = []
526+
527+rangesForBinding' :: String -> LIE GhcPs -> [SrcSpan]
528+rangesForBinding' b (L l x@IEVar{}) | showSDocUnsafe (ppr x) == b = [l]
529+rangesForBinding' b (L l x@IEThingAbs{}) | showSDocUnsafe (ppr x) == b = [l]
530+rangesForBinding' b (L l (IEThingAll x)) | showSDocUnsafe (ppr x) == b = [l]
531+rangesForBinding' b (L l (IEThingWith thing _ inners labels))
532+ | showSDocUnsafe (ppr thing) == b = [l]
533+ | otherwise =
534+ [ l' | L l' x <- inners, showSDocUnsafe (ppr x) == b] ++
535+ [ l' | L l' x <- labels, showSDocUnsafe (ppr x) == b]
536+rangesForBinding' _ _ = []
537+
538+-- | Extends an import list with a new binding.
539+-- Assumes an import statement of the form:
540+-- import (qualified) A (..) ..
541+-- Places the new binding first, preserving whitespace.
542+-- Copes with multi-line import lists
543+addBindingToImportList :: T.Text -> T.Text -> T.Text
544+addBindingToImportList binding importLine = case T.breakOn "(" importLine of
545+ (pre, T.uncons -> Just (_, rest)) ->
546+ case T.uncons (T.dropWhile isSpace rest) of
547+ Just (')', _) -> T.concat [pre, "(", binding, rest]
548+ _ -> T.concat [pre, "(", binding, ", ", rest]
549+ _ ->
550+ error
551+ $ "importLine does not have the expected structure: "
552+ <> T.unpack importLine
553+
554+-- | Returns Just (the submatches) for the first capture, or Nothing.
555+matchRegex :: T.Text -> T.Text -> Maybe [T.Text]
556+matchRegex message regex = case unifySpaces message =~~ regex of
557+ Just (_ :: T.Text, _ :: T.Text, _ :: T.Text, bindings) -> Just bindings
558+ Nothing -> Nothing
559+
560+setHandlersCodeLens :: PartialHandlers c
561+setHandlersCodeLens = PartialHandlers $ \WithMessage{..} x -> return x{
562+ LSP.codeLensHandler = withResponse RspCodeLens codeLens,
563+ LSP.executeCommandHandler = withResponseAndRequest RspExecuteCommand ReqApplyWorkspaceEdit executeAddSignatureCommand
564+ }
565+
566+filterNewlines :: T.Text -> T.Text
567+filterNewlines = T.concat . T.lines
568+
569+unifySpaces :: T.Text -> T.Text
570+unifySpaces = T.unwords . T.words
src/Development/IDE/Plugin/CodeAction/PositionIndexed.hsnew file+112
@@ -0,0 +1,112 @@
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.hsnew file+61
@@ -0,0 +1,61 @@
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.hsnew file+61
@@ -0,0 +1,61 @@
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.hsnew file+103
@@ -0,0 +1,103 @@
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/Plugin/Completions/Logic.hsnew file+530
@@ -0,0 +1,530 @@
1+{-# LANGUAGE CPP #-}
2+#include "ghc-api-version.h"
3+-- Mostly taken from "haskell-ide-engine"
4+module Development.IDE.Plugin.Completions.Logic (
5+ CachedCompletions
6+, cacheDataProducer
7+, WithSnippets(..)
8+,getCompletions
9+) where
10+
11+import Control.Applicative
12+import Data.Char (isSpace, isUpper)
13+import Data.Generics
14+import Data.List.Extra as List hiding (stripPrefix)
15+import qualified Data.Map as Map
16+import Data.Maybe (fromMaybe, mapMaybe)
17+import qualified Data.Text as T
18+import qualified Text.Fuzzy as Fuzzy
19+
20+import GHC
21+import HscTypes
22+import Name
23+import RdrName
24+import TcRnTypes
25+import Type
26+import Var
27+import Packages
28+import 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
35+
36+import Language.Haskell.LSP.Types
37+import Language.Haskell.LSP.Types.Capabilities
38+import qualified Language.Haskell.LSP.VFS as VFS
39+import Development.IDE.Plugin.Completions.Types
40+import Development.IDE.Spans.Documentation
41+import Development.IDE.GHC.Error
42+import Development.IDE.Types.Options
43+import Development.IDE.Spans.Common
44+import Development.IDE.GHC.Util
45+
46+-- From haskell-ide-engine/hie-plugin-api/Haskell/Ide/Engine/Context.hs
47+
48+-- | A context of a declaration in the program
49+-- e.g. is the declaration a type declaration or a value declaration
50+-- Used for determining which code completions to show
51+-- TODO: expand this with more contexts like classes or instances for
52+-- smarter code completion
53+data Context = TypeContext
54+ | ValueContext
55+ | ModuleContext String -- ^ module context with module name
56+ | ImportContext String -- ^ import context with module name
57+ | ImportListContext String -- ^ import list context with module name
58+ | ImportHidingContext String -- ^ import hiding context with module name
59+ | ExportContext -- ^ List of exported identifiers from the current module
60+ deriving (Show, Eq)
61+
62+-- | Generates a map of where the context is a type and where the context is a value
63+-- i.e. where are the value decls and the type decls
64+getCContext :: Position -> ParsedModule -> Maybe Context
65+getCContext pos pm
66+ | Just (L r modName) <- moduleHeader
67+ , pos `isInsideSrcSpan` r
68+ = Just (ModuleContext (moduleNameString modName))
69+
70+ | Just (L r _) <- exportList
71+ , pos `isInsideSrcSpan` r
72+ = Just ExportContext
73+
74+ | Just ctx <- something (Nothing `mkQ` go `extQ` goInline) decl
75+ = Just ctx
76+
77+ | Just ctx <- something (Nothing `mkQ` importGo) imports
78+ = Just ctx
79+
80+ | otherwise
81+ = Nothing
82+
83+ where decl = hsmodDecls $ unLoc $ pm_parsed_source pm
84+ moduleHeader = hsmodName $ unLoc $ pm_parsed_source pm
85+ exportList = hsmodExports $ unLoc $ pm_parsed_source pm
86+ imports = hsmodImports $ unLoc $ pm_parsed_source pm
87+
88+ go :: LHsDecl GhcPs -> Maybe Context
89+ go (L r SigD {})
90+ | pos `isInsideSrcSpan` r = Just TypeContext
91+ | otherwise = Nothing
92+ go (L r GHC.ValD {})
93+ | pos `isInsideSrcSpan` r = Just ValueContext
94+ | otherwise = Nothing
95+ go _ = Nothing
96+
97+ goInline :: GHC.LHsType GhcPs -> Maybe Context
98+ goInline (GHC.L r _)
99+ | pos `isInsideSrcSpan` r = Just TypeContext
100+ goInline _ = Nothing
101+
102+ importGo :: GHC.LImportDecl GhcPs -> Maybe Context
103+ importGo (L r impDecl)
104+ | pos `isInsideSrcSpan` r
105+ = importInline importModuleName (ideclHiding impDecl)
106+ <|> Just (ImportContext importModuleName)
107+
108+ | otherwise = Nothing
109+ where importModuleName = moduleNameString $ unLoc $ ideclName impDecl
110+
111+ importInline :: String -> Maybe (Bool, GHC.Located [LIE GhcPs]) -> Maybe Context
112+ importInline modName (Just (True, L r _))
113+ | pos `isInsideSrcSpan` r = Just $ ImportHidingContext modName
114+ | otherwise = Nothing
115+ importInline modName (Just (False, L r _))
116+ | pos `isInsideSrcSpan` r = Just $ ImportListContext modName
117+ | otherwise = Nothing
118+ importInline _ _ = Nothing
119+
120+occNameToComKind :: Maybe T.Text -> OccName -> CompletionItemKind
121+occNameToComKind ty oc
122+ | isVarOcc oc = case occNameString oc of
123+ i:_ | isUpper i -> CiConstructor
124+ _ -> CiFunction
125+ | isTcOcc oc = case ty of
126+ Just t
127+ | "Constraint" `T.isSuffixOf` t
128+ -> CiClass
129+ _ -> CiStruct
130+ | isDataOcc oc = CiConstructor
131+ | otherwise = CiVariable
132+
133+mkCompl :: IdeOptions -> CompItem -> CompletionItem
134+mkCompl IdeOptions{..} CI{origName,importedFrom,thingType,label,isInfix,docs} =
135+ CompletionItem label kind (List []) ((colon <>) <$> typeText)
136+ (Just $ CompletionDocMarkup $ MarkupContent MkMarkdown $ T.intercalate sectionSeparator docs')
137+ Nothing Nothing Nothing Nothing (Just insertText) (Just Snippet)
138+ Nothing Nothing Nothing Nothing Nothing
139+ where kind = Just $ occNameToComKind typeText $ occName origName
140+ insertText = case isInfix of
141+ Nothing -> case getArgText <$> thingType of
142+ Nothing -> label
143+ Just argText -> label <> " " <> argText
144+ Just LeftSide -> label <> "`"
145+
146+ Just Surrounded -> label
147+ typeText
148+ | Just t <- thingType = Just . stripForall $ T.pack (showGhc t)
149+ | otherwise = Nothing
150+ docs' = ("*Defined in '" <> importedFrom <> "'*\n") : spanDocToMarkdown docs
151+ colon = if optNewColonConvention then ": " else ":: "
152+
153+stripForall :: T.Text -> T.Text
154+stripForall t
155+ | T.isPrefixOf "forall" t =
156+ -- We drop 2 to remove the '.' and the space after it
157+ T.drop 2 (T.dropWhile (/= '.') t)
158+ | otherwise = t
159+
160+getArgText :: Type -> T.Text
161+getArgText typ = argText
162+ where
163+ argTypes = getArgs typ
164+ argText :: T.Text
165+ argText = mconcat $ List.intersperse " " $ zipWithFrom snippet 1 argTypes
166+ snippet :: Int -> Type -> T.Text
167+ snippet i t = T.pack $ "${" <> show i <> ":" <> showGhc t <> "}"
168+ getArgs :: Type -> [Type]
169+ getArgs t
170+ | isPredTy t = []
171+ | isDictTy t = []
172+ | isForAllTy t = getArgs $ snd (splitForAllTys t)
173+ | isFunTy t =
174+ let (args, ret) = splitFunTys t
175+ in if isForAllTy ret
176+ then getArgs ret
177+ else Prelude.filter (not . isDictTy) args
178+ | 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
183+ | isCoercionTy t = maybe [] (getArgs . snd) (splitCoercionType_maybe t)
184+#endif
185+ | otherwise = []
186+
187+mkModCompl :: T.Text -> CompletionItem
188+mkModCompl label =
189+ CompletionItem label (Just CiModule) (List []) Nothing
190+ Nothing Nothing Nothing Nothing Nothing Nothing Nothing
191+ Nothing Nothing Nothing Nothing Nothing
192+
193+mkImportCompl :: T.Text -> T.Text -> CompletionItem
194+mkImportCompl enteredQual label =
195+ CompletionItem m (Just CiModule) (List []) (Just label)
196+ Nothing Nothing Nothing Nothing Nothing Nothing Nothing
197+ Nothing Nothing Nothing Nothing Nothing
198+ where
199+ m = fromMaybe "" (T.stripPrefix enteredQual label)
200+
201+mkExtCompl :: T.Text -> CompletionItem
202+mkExtCompl label =
203+ CompletionItem label (Just CiKeyword) (List []) Nothing
204+ Nothing Nothing Nothing Nothing Nothing Nothing Nothing
205+ Nothing Nothing Nothing Nothing Nothing
206+
207+mkPragmaCompl :: T.Text -> T.Text -> CompletionItem
208+mkPragmaCompl label insertText =
209+ CompletionItem label (Just CiKeyword) (List []) Nothing
210+ Nothing Nothing Nothing Nothing Nothing (Just insertText) (Just Snippet)
211+ Nothing Nothing Nothing Nothing Nothing
212+
213+cacheDataProducer :: HscEnv -> TypecheckedModule -> [ParsedModule] -> IO CachedCompletions
214+cacheDataProducer packageState tm deps = do
215+ let parsedMod = tm_parsed_module tm
216+ dflags = hsc_dflags packageState
217+ curMod = moduleName $ ms_mod $ pm_mod_summary parsedMod
218+ Just (_,limports,_,_) = tm_renamed_source tm
219+
220+ iDeclToModName :: ImportDecl name -> ModuleName
221+ iDeclToModName = unLoc . ideclName
222+
223+ showModName :: ModuleName -> T.Text
224+ showModName = T.pack . moduleNameString
225+
226+ asNamespace :: ImportDecl name -> ModuleName
227+ asNamespace imp = maybe (iDeclToModName imp) GHC.unLoc (ideclAs imp)
228+ -- Full canonical names of imported modules
229+ importDeclerations = map unLoc limports
230+
231+ -- The list of all importable Modules from all packages
232+ moduleNames = map showModName (listVisibleModuleNames dflags)
233+
234+ -- The given namespaces for the imported modules (ie. full name, or alias if used)
235+ allModNamesAsNS = map (showModName . asNamespace) importDeclerations
236+
237+ typeEnv = tcg_type_env $ fst $ tm_internals_ tm
238+ rdrEnv = tcg_rdr_env $ fst $ tm_internals_ tm
239+ rdrElts = globalRdrEnvElts rdrEnv
240+
241+ foldMapM :: (Foldable f, Monad m, Monoid b) => (a -> m b) -> f a -> m b
242+ foldMapM f xs = foldr step return xs mempty where
243+ step x r z = f x >>= \y -> r $! z `mappend` y
244+
245+ getCompls :: [GlobalRdrElt] -> IO ([CompItem],QualCompls)
246+ getCompls = foldMapM getComplsForOne
247+
248+ getComplsForOne :: GlobalRdrElt -> IO ([CompItem],QualCompls)
249+ getComplsForOne (GRE n _ True _) =
250+ case lookupTypeEnv typeEnv n of
251+ Just tt -> case safeTyThingId tt of
252+ Just var -> (\x -> ([x],mempty)) <$> varToCompl var
253+ Nothing -> (\x -> ([x],mempty)) <$> toCompItem curMod n
254+ Nothing -> (\x -> ([x],mempty)) <$> toCompItem curMod n
255+ getComplsForOne (GRE n _ False prov) =
256+ flip foldMapM (map is_decl prov) $ \spec -> do
257+ compItem <- toCompItem (is_mod spec) n
258+ let unqual
259+ | is_qual spec = []
260+ | otherwise = [compItem]
261+ qual
262+ | is_qual spec = Map.singleton asMod [compItem]
263+ | otherwise = Map.fromList [(asMod,[compItem]),(origMod,[compItem])]
264+ asMod = showModName (is_as spec)
265+ origMod = showModName (is_mod spec)
266+ return (unqual,QualCompls qual)
267+
268+ varToCompl :: Var -> IO CompItem
269+ varToCompl var = do
270+ let typ = Just $ varType var
271+ name = Var.varName var
272+ label = T.pack $ showGhc name
273+ docs <- evalGhcEnv packageState $ getDocumentationTryGhc (tm_parsed_module tm : deps) name
274+ return $ CI name (showModName curMod) typ label Nothing docs
275+
276+ toCompItem :: ModuleName -> Name -> IO CompItem
277+ toCompItem mn n = do
278+ docs <- evalGhcEnv packageState $ getDocumentationTryGhc (tm_parsed_module tm : deps) n
279+-- lookupName uses runInteractiveHsc, i.e., GHCi stuff which does not work with GHCi
280+-- and leads to fun errors like "Cannot continue after interface file error".
281+#ifdef GHC_LIB
282+ let ty = Right Nothing
283+#else
284+ ty <- evalGhcEnv packageState $ catchSrcErrors "completion" $ do
285+ name' <- lookupName n
286+ return $ name' >>= safeTyThingType
287+#endif
288+ return $ CI n (showModName mn) (either (const Nothing) id ty) (T.pack $ showGhc n) Nothing docs
289+
290+ (unquals,quals) <- getCompls rdrElts
291+
292+ return $ CC
293+ { allModNamesAsNS = allModNamesAsNS
294+ , unqualCompls = unquals
295+ , qualCompls = quals
296+ , importableModules = moduleNames
297+ }
298+
299+newtype WithSnippets = WithSnippets Bool
300+
301+toggleSnippets :: ClientCapabilities -> WithSnippets -> CompletionItem -> CompletionItem
302+toggleSnippets ClientCapabilities { _textDocument } (WithSnippets with) x
303+ | with && supported = x
304+ | otherwise = x { _insertTextFormat = Just PlainText
305+ , _insertText = Nothing
306+ }
307+ where supported = Just True == (_textDocument >>= _completion >>= _completionItem >>= _snippetSupport)
308+
309+-- | Returns the cached completions for the given module and position.
310+getCompletions :: IdeOptions -> CachedCompletions -> ParsedModule -> VFS.PosPrefixInfo -> ClientCapabilities -> WithSnippets -> IO [CompletionItem]
311+getCompletions ideOpts CC { allModNamesAsNS, unqualCompls, qualCompls, importableModules }
312+ pm prefixInfo caps withSnippets = do
313+ let VFS.PosPrefixInfo { VFS.fullLine, VFS.prefixModule, VFS.prefixText } = prefixInfo
314+ enteredQual = if T.null prefixModule then "" else prefixModule <> "."
315+ fullPrefix = enteredQual <> prefixText
316+
317+ {- correct the position by moving 'foo :: Int -> String -> '
318+ ^
319+ to 'foo :: Int -> String -> '
320+ ^
321+ -}
322+ pos =
323+ let Position l c = VFS.cursorPos prefixInfo
324+ typeStuff = [isSpace, (`elem` (">-." :: String))]
325+ stripTypeStuff = T.dropWhileEnd (\x -> any (\f -> f x) typeStuff)
326+ -- if oldPos points to
327+ -- foo -> bar -> baz
328+ -- ^
329+ -- Then only take the line up to there, discard '-> bar -> baz'
330+ partialLine = T.take c fullLine
331+ -- drop characters used when writing incomplete type sigs
332+ -- like '-> '
333+ d = T.length fullLine - T.length (stripTypeStuff partialLine)
334+ in Position l (c - d)
335+
336+ filtModNameCompls =
337+ map mkModCompl
338+ $ mapMaybe (T.stripPrefix enteredQual)
339+ $ Fuzzy.simpleFilter fullPrefix allModNamesAsNS
340+
341+ filtCompls = map Fuzzy.original $ Fuzzy.filter prefixText ctxCompls "" "" label False
342+ where
343+ isTypeCompl = isTcOcc . occName . origName
344+ -- 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
350+ -- Add whether the text to insert has backticks
351+ ctxCompls = map (\comp -> comp { isInfix = infixCompls }) ctxCompls'
352+
353+ infixCompls :: Maybe Backtick
354+ infixCompls = isUsedAsInfix fullLine prefixModule prefixText (VFS.cursorPos prefixInfo)
355+
356+ compls = if T.null prefixModule
357+ then unqualCompls
358+ else Map.findWithDefault [] prefixModule $ getQualCompls qualCompls
359+
360+ filtListWith f list =
361+ [ f label
362+ | label <- Fuzzy.simpleFilter fullPrefix list
363+ , enteredQual `T.isPrefixOf` label
364+ ]
365+
366+ filtListWithSnippet f list suffix =
367+ [ toggleSnippets caps withSnippets (f label (snippet <> suffix))
368+ | (snippet, label) <- list
369+ , Fuzzy.test fullPrefix label
370+ ]
371+
372+ filtImportCompls = filtListWith (mkImportCompl enteredQual) importableModules
373+ filtPragmaCompls = filtListWithSnippet mkPragmaCompl validPragmas
374+ filtOptsCompls = filtListWith mkExtCompl
375+ filtKeywordCompls
376+ | T.null prefixModule = filtListWith mkExtCompl (optKeywords ideOpts)
377+ | otherwise = []
378+
379+ stripLeading :: Char -> String -> String
380+ stripLeading _ [] = []
381+ stripLeading c (s:ss)
382+ | s == c = ss
383+ | otherwise = s:ss
384+
385+ result
386+ | "import " `T.isPrefixOf` fullLine
387+ = filtImportCompls
388+ | "{-# language" `T.isPrefixOf` T.toLower fullLine
389+ = filtOptsCompls languagesAndExts
390+ | "{-# options_ghc" `T.isPrefixOf` T.toLower fullLine
391+ = filtOptsCompls (map (T.pack . stripLeading '-') $ flagsForCompletion False)
392+ | "{-# " `T.isPrefixOf` fullLine
393+ = filtPragmaCompls (pragmaSuffix fullLine)
394+ | otherwise
395+ = filtModNameCompls ++ map (toggleSnippets caps withSnippets
396+ . mkCompl ideOpts . stripAutoGenerated) filtCompls
397+ ++ filtKeywordCompls
398+
399+ return result
400+
401+-- The supported languages and extensions
402+languagesAndExts :: [T.Text]
403+#if MIN_GHC_API_VERSION(8,10,0)
404+languagesAndExts = map T.pack $ DynFlags.supportedLanguagesAndExtensions ( PlatformMini ArchUnknown OSUnknown )
405+#else
406+languagesAndExts = map T.pack DynFlags.supportedLanguagesAndExtensions
407+#endif
408+
409+-- ---------------------------------------------------------------------
410+-- helper functions for pragmas
411+-- ---------------------------------------------------------------------
412+
413+validPragmas :: [(T.Text, T.Text)]
414+validPragmas =
415+ [ ("LANGUAGE ${1:extension}" , "LANGUAGE")
416+ , ("OPTIONS_GHC -${1:option}" , "OPTIONS_GHC")
417+ , ("INLINE ${1:function}" , "INLINE")
418+ , ("NOINLINE ${1:function}" , "NOINLINE")
419+ , ("INLINABLE ${1:function}" , "INLINABLE")
420+ , ("WARNING ${1:message}" , "WARNING")
421+ , ("DEPRECATED ${1:message}" , "DEPRECATED")
422+ , ("ANN ${1:annotation}" , "ANN")
423+ , ("RULES" , "RULES")
424+ , ("SPECIALIZE ${1:function}" , "SPECIALIZE")
425+ , ("SPECIALIZE INLINE ${1:function}", "SPECIALIZE INLINE")
426+ ]
427+
428+pragmaSuffix :: T.Text -> T.Text
429+pragmaSuffix fullLine
430+ | "}" `T.isSuffixOf` fullLine = mempty
431+ | otherwise = " #-}"
432+
433+-- ---------------------------------------------------------------------
434+-- helper functions for infix backticks
435+-- ---------------------------------------------------------------------
436+
437+hasTrailingBacktick :: T.Text -> Position -> Bool
438+hasTrailingBacktick line Position { _character }
439+ | T.length line > _character = (line `T.index` _character) == '`'
440+ | otherwise = False
441+
442+isUsedAsInfix :: T.Text -> T.Text -> T.Text -> Position -> Maybe Backtick
443+isUsedAsInfix line prefixMod prefixText pos
444+ | hasClosingBacktick && hasOpeningBacktick = Just Surrounded
445+ | hasOpeningBacktick = Just LeftSide
446+ | otherwise = Nothing
447+ where
448+ hasOpeningBacktick = openingBacktick line prefixMod prefixText pos
449+ hasClosingBacktick = hasTrailingBacktick line pos
450+
451+openingBacktick :: T.Text -> T.Text -> T.Text -> Position -> Bool
452+openingBacktick line prefixModule prefixText Position { _character }
453+ | backtickIndex < 0 = False
454+ | otherwise = (line `T.index` backtickIndex) == '`'
455+ where
456+ backtickIndex :: Int
457+ backtickIndex =
458+ let
459+ prefixLength = T.length prefixText
460+ moduleLength = if prefixModule == ""
461+ then 0
462+ else T.length prefixModule + 1 {- Because of "." -}
463+ in
464+ -- Points to the first letter of either the module or prefix text
465+ _character - (prefixLength + moduleLength) - 1
466+
467+
468+-- ---------------------------------------------------------------------
469+
470+-- | Under certain circumstance GHC generates some extra stuff that we
471+-- don't want in the autocompleted symbols
472+stripAutoGenerated :: CompItem -> CompItem
473+stripAutoGenerated ci =
474+ ci {label = stripPrefix (label ci)}
475+ {- When e.g. DuplicateRecordFields is enabled, compiler generates
476+ names like "$sel:accessor:One" and "$sel:accessor:Two" to disambiguate record selectors
477+ https://ghc.haskell.org/trac/ghc/wiki/Records/OverloadedRecordFields/DuplicateRecordFields#Implementation
478+ -}
479+
480+-- TODO: Turn this into an alex lexer that discards prefixes as if they were whitespace.
481+
482+stripPrefix :: T.Text -> T.Text
483+stripPrefix name = T.takeWhile (/=':') $ go prefixes
484+ where
485+ go [] = name
486+ go (p:ps)
487+ | T.isPrefixOf p name = T.drop (T.length p) name
488+ | otherwise = go ps
489+
490+-- | Prefixes that can occur in a GHC OccName
491+prefixes :: [T.Text]
492+prefixes =
493+ [
494+ -- long ones
495+ "$con2tag_"
496+ , "$tag2con_"
497+ , "$maxtag_"
498+
499+ -- four chars
500+ , "$sel:"
501+ , "$tc'"
502+
503+ -- three chars
504+ , "$dm"
505+ , "$co"
506+ , "$tc"
507+ , "$cp"
508+ , "$fx"
509+
510+ -- two chars
511+ , "$W"
512+ , "$w"
513+ , "$m"
514+ , "$b"
515+ , "$c"
516+ , "$d"
517+ , "$i"
518+ , "$s"
519+ , "$f"
520+ , "$r"
521+ , "C:"
522+ , "N:"
523+ , "D:"
524+ , "$p"
525+ , "$L"
526+ , "$f"
527+ , "$t"
528+ , "$c"
529+ , "$m"
530+ ]
src/Development/IDE/Plugin/Completions/Types.hsnew file+58
@@ -0,0 +1,58 @@
1+module Development.IDE.Plugin.Completions.Types (
2+ module Development.IDE.Plugin.Completions.Types
3+) where
4+
5+import Control.DeepSeq
6+import qualified Data.Map as Map
7+import qualified Data.Text as T
8+import GHC
9+
10+import Development.IDE.Spans.Common
11+
12+-- From haskell-ide-engine/src/Haskell/Ide/Engine/LSP/Completions.hs
13+
14+data Backtick = Surrounded | LeftSide deriving Show
15+data CompItem = CI
16+ { origName :: Name -- ^ Original name, such as Maybe, //, or find.
17+ , importedFrom :: T.Text -- ^ From where this item is imported from.
18+ , thingType :: Maybe Type -- ^ Available type information.
19+ , label :: T.Text -- ^ Label to display to the user.
20+ , isInfix :: Maybe Backtick -- ^ Did the completion happen
21+ -- in the context of an infix notation.
22+ , docs :: SpanDoc -- ^ Available documentation.
23+ }
24+instance Show CompItem where
25+ show CI { .. } = "CompItem { origName = \"" ++ showGhc origName ++ "\""
26+ ++ ", importedFrom = " ++ show importedFrom
27+ ++ ", thingType = " ++ show (fmap showGhc thingType)
28+ ++ ", label = " ++ show label
29+ ++ ", isInfix = " ++ show isInfix
30+ ++ ", docs = " ++ show docs
31+ ++ " } "
32+instance Eq CompItem where
33+ ci1 == ci2 = origName ci1 == origName ci2
34+instance Ord CompItem where
35+ compare ci1 ci2 = origName ci1 `compare` origName ci2
36+
37+-- Associates a module's qualifier with its members
38+newtype QualCompls
39+ = QualCompls { getQualCompls :: Map.Map T.Text [CompItem] }
40+ deriving Show
41+instance Semigroup QualCompls where
42+ (QualCompls a) <> (QualCompls b) = QualCompls $ Map.unionWith (++) a b
43+instance Monoid QualCompls where
44+ mempty = QualCompls Map.empty
45+ mappend = (Prelude.<>)
46+
47+-- | End result of the completions
48+data CachedCompletions = CC
49+ { allModNamesAsNS :: [T.Text] -- ^ All module names in scope.
50+ -- Prelude is a single module
51+ , unqualCompls :: [CompItem] -- ^ All Possible completion items
52+ , qualCompls :: QualCompls -- ^ Completion items associated to
53+ -- to a specific module name.
54+ , importableModules :: [T.Text] -- ^ All modules that may be imported.
55+ } deriving Show
56+
57+instance NFData CachedCompletions where
58+ rnf = rwhnf
src/Development/IDE/Spans/AtPoint.hs+91−56
@@ -1,7 +1,6 @@
1−-- Copyright (c) 2019 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
1+-- Copyright (c) 2019 The DAML Authors. All rights reserved.
2-- SPDX-License-Identifier: Apache-2.0
2-- SPDX-License-Identifier: Apache-2.0
3
3
4−{-# LANGUAGE OverloadedStrings #-}
5-- | Gives information about symbols at a given point in DAML files.
4-- | Gives information about symbols at a given point in DAML files.
6-- These are all pure functions that should execute quickly.
5-- These are all pure functions that should execute quickly.
7module Development.IDE.Spans.AtPoint (
6module Development.IDE.Spans.AtPoint (
@@ -9,26 +8,24 @@ module Development.IDE.Spans.AtPoint (
9 , gotoDefinition
8 , gotoDefinition
10 ) where
9 ) where
11
10
12−import Development.IDE.Spans.Documentation
13import Development.IDE.GHC.Error
11import Development.IDE.GHC.Error
14import Development.IDE.GHC.Orphans()
12import Development.IDE.GHC.Orphans()
15import Development.IDE.Types.Location
13import Development.IDE.Types.Location
16
14
17-- DAML compiler and infrastructure
15-- DAML compiler and infrastructure
18−import Development.Shake
19−import Development.IDE.GHC.Util
20import Development.IDE.GHC.Compat
16import Development.IDE.GHC.Compat
21import Development.IDE.Types.Options
17import Development.IDE.Types.Options
22−import Development.IDE.Spans.Type as SpanInfo
18+import Development.IDE.Spans.Type as SpanInfo
19+import Development.IDE.Spans.Common (spanDocToMarkdown)
23
20
24-- GHC API imports
21-- GHC API imports
25−import Avail
26−import GHC
27import DynFlags
22import DynFlags
28import FastString
23import FastString
29import Name
24import Name
30import Outputable hiding ((<>))
25import Outputable hiding ((<>))
31import SrcLoc
26import SrcLoc
27+import Type
28+import VarSet
32
29
33import Control.Monad.Extra
30import Control.Monad.Extra
34import Control.Monad.Trans.Maybe
31import Control.Monad.Trans.Maybe
@@ -40,96 +37,134 @@ import qualified Data.Text as T
40-- | Locate the definition of the name at a given position.
37-- | Locate the definition of the name at a given position.
41gotoDefinition
38gotoDefinition
42 :: MonadIO m
39 :: MonadIO m
43− => (FilePath -> m (Maybe HieFile))
40+ => (Module -> m (Maybe (HieFile, FilePath)))
44 -> IdeOptions
41 -> IdeOptions
45− -> HscEnv
46 -> [SpanInfo]
42 -> [SpanInfo]
47 -> Position
43 -> Position
48 -> m (Maybe Location)
44 -> m (Maybe Location)
49−gotoDefinition getHieFile ideOpts pkgState srcSpans pos =
45+gotoDefinition getHieFile ideOpts srcSpans pos =
50− listToMaybe <$> locationsAtPoint getHieFile ideOpts pkgState pos srcSpans
46+ listToMaybe <$> locationsAtPoint getHieFile ideOpts pos srcSpans
51
47
52-- | Synopsis for the name at a given position.
48-- | Synopsis for the name at a given position.
53atPoint
49atPoint
54 :: IdeOptions
50 :: IdeOptions
55− -> [TypecheckedModule]
51+ -> SpansInfo
56− -> [SpanInfo]
57 -> Position
52 -> Position
58 -> Maybe (Maybe Range, [T.Text])
53 -> Maybe (Maybe Range, [T.Text])
59−atPoint IdeOptions{..} tcs srcSpans pos = do
54+atPoint IdeOptions{..} (SpansInfo srcSpans cntsSpans) pos = do
60− SpanInfo{..} <- listToMaybe $ orderSpans $ spansAtPoint pos srcSpans
55+ firstSpan <- listToMaybe $ deEmpasizeGeneratedEqShow $ spansAtPoint pos srcSpans
61− ty <- spaninfoType
56+ let constraintsAtPoint = mapMaybe spaninfoType (spansAtPoint pos cntsSpans)
62− let mbName = getNameM spaninfoSource
63− mbDefinedAt = fmap (\name -> "**Defined " <> T.pack (showSDocUnsafe $ pprNameDefnLoc name) <> "**\n") mbName
64− docInfo = maybe [] (\name -> getDocumentation name tcs) mbName
65− range = Range
66− (Position spaninfoStartLine spaninfoStartCol)
67− (Position spaninfoEndLine spaninfoEndCol)
68− colon = if optNewColonConvention then ":" else "::"
69− wrapLanguageSyntax x = T.unlines [ "```" <> T.pack optLanguageSyntax, x, "```"]
70− typeSig = wrapLanguageSyntax $ case mbName of
71− Nothing -> colon <> " " <> showName ty
72− Just name ->
73− let modulePrefix = maybe "" (<> ".") (getModuleNameAsText name)
74− in modulePrefix <> showName name <> "\n " <> colon <> " " <> showName ty
75− hoverInfo = docInfo <> [typeSig] <> maybeToList mbDefinedAt
76− return (Just range, hoverInfo)
57+ return (Just (range firstSpan), hoverInfo firstSpan constraintsAtPoint)
77 where
58 where
59+ -- Hover info for types, classes, type variables
60+ hoverInfo SpanInfo{spaninfoType = Nothing , spaninfoDocs = docs , ..} _ =
61+ (wrapLanguageSyntax <$> name) <> location <> spanDocToMarkdown docs
62+ where
63+ name = [maybe shouldNotHappen showName mbName]
64+ location = [maybe shouldNotHappen definedAt mbName]
65+ shouldNotHappen = "ghcide: did not expect a type level component without a name"
66+ mbName = getNameM spaninfoSource
67+
68+ -- Hover info for values/data
69+ hoverInfo SpanInfo{spaninfoType = (Just typ), spaninfoDocs = docs , ..} cnts =
70+ (wrapLanguageSyntax <$> nameOrSource) <> location <> spanDocToMarkdown docs
71+ where
72+ mbName = getNameM spaninfoSource
73+ expr = case spaninfoSource of
74+ Named n -> qualifyNameIfPossible n
75+ Lit l -> crop $ T.pack l
76+ _ -> ""
77+ nameOrSource = [expr <> "\n" <> typeAnnotation]
78+ qualifyNameIfPossible name' = modulePrefix <> showName name'
79+ where modulePrefix = maybe "" (<> ".") (getModuleNameAsText name')
80+ location = [maybe "" definedAt mbName]
81+
82+ thisFVs = tyCoVarsOfType typ
83+ constraintsOverFVs = filter (\cnt -> not (tyCoVarsOfType cnt `disjointVarSet` thisFVs)) cnts
84+ constraintsT = T.intercalate ", " (map showName constraintsOverFVs)
85+
86+ typeAnnotation = case constraintsOverFVs of
87+ [] -> colon <> showName typ
88+ [_] -> colon <> constraintsT <> "\n=> " <> showName typ
89+ _ -> colon <> "(" <> constraintsT <> ")\n=> " <> showName typ
90+
91+ definedAt name = "*Defined " <> T.pack (showSDocUnsafe $ pprNameDefnLoc name) <> "*\n"
92+
93+ crop txt
94+ | T.length txt > 50 = T.take 46 txt <> " ..."
95+ | otherwise = txt
96+
97+ range SpanInfo{..} = Range
98+ (Position spaninfoStartLine spaninfoStartCol)
99+ (Position spaninfoEndLine spaninfoEndCol)
100+
101+ colon = if optNewColonConvention then ": " else ":: "
102+ wrapLanguageSyntax x = T.unlines [ "```" <> T.pack optLanguageSyntax, x, "```"]
103+
78 -- NOTE(RJR): This is a bit hacky.
104 -- NOTE(RJR): This is a bit hacky.
79 -- We don't want to show the user type signatures generated from Eq and Show
105 -- We don't want to show the user type signatures generated from Eq and Show
80 -- instances, as they do not appear in the source program.
106 -- instances, as they do not appear in the source program.
81 -- However the user could have written an `==` or `show` function directly,
107 -- However the user could have written an `==` or `show` function directly,
82 -- in which case we still want to show information for that.
108 -- in which case we still want to show information for that.
83 -- Hence we just move such information later in the list of spans.
109 -- Hence we just move such information later in the list of spans.
84− orderSpans :: [SpanInfo] -> [SpanInfo]
110+ deEmpasizeGeneratedEqShow :: [SpanInfo] -> [SpanInfo]
85− orderSpans = uncurry (++) . partition (not . isTypeclassDeclSpan)
111+ deEmpasizeGeneratedEqShow = uncurry (++) . partition (not . isTypeclassDeclSpan)
86 isTypeclassDeclSpan :: SpanInfo -> Bool
112 isTypeclassDeclSpan :: SpanInfo -> Bool
87 isTypeclassDeclSpan spanInfo =
113 isTypeclassDeclSpan spanInfo =
88 case getNameM (spaninfoSource spanInfo) of
114 case getNameM (spaninfoSource spanInfo) of
89− Just name -> any (`isInfixOf` show name) ["==", "showsPrec"]
115+ Just name -> any (`isInfixOf` getOccString name) ["==", "showsPrec"]
90 Nothing -> False
116 Nothing -> False
91
117
92−locationsAtPoint :: forall m . MonadIO m => (FilePath -> m (Maybe HieFile)) -> IdeOptions -> HscEnv -> Position -> [SpanInfo] -> m [Location]
118+locationsAtPoint
119+ :: forall m
120+ . MonadIO m
121+ => (Module -> m (Maybe (HieFile, FilePath)))
122+ -> IdeOptions
123+ -> Position
124+ -> [SpanInfo]
125+ -> m [Location]
93−locationsAtPoint getHieFile IdeOptions{..} pkgState pos =
126+locationsAtPoint getHieFile _ideOptions pos =
94− fmap (map srcSpanToLocation) .
95− mapMaybeM (getSpan . spaninfoSource) .
127+ fmap (map srcSpanToLocation) . mapMaybeM (getSpan . spaninfoSource) . spansAtPoint pos
96− spansAtPoint pos
97 where getSpan :: SpanSource -> m (Maybe SrcSpan)
128 where getSpan :: SpanSource -> m (Maybe SrcSpan)
98 getSpan NoSource = pure Nothing
129 getSpan NoSource = pure Nothing
99 getSpan (SpanS sp) = pure $ Just sp
130 getSpan (SpanS sp) = pure $ Just sp
131+ getSpan (Lit _) = pure Nothing
100 getSpan (Named name) = case nameSrcSpan name of
132 getSpan (Named name) = case nameSrcSpan name of
101 sp@(RealSrcSpan _) -> pure $ Just sp
133 sp@(RealSrcSpan _) -> pure $ Just sp
102 sp@(UnhelpfulSpan _) -> runMaybeT $ do
134 sp@(UnhelpfulSpan _) -> runMaybeT $ do
103 guard (sp /= wiredInSrcSpan)
135 guard (sp /= wiredInSrcSpan)
104− -- This case usually arises when the definition is in an external package.
136+ -- This case usually arises when the definition is in an external package (DAML only).
105 -- In this case the interface files contain garbage source spans
137 -- In this case the interface files contain garbage source spans
106 -- so we instead read the .hie files to get useful source spans.
138 -- so we instead read the .hie files to get useful source spans.
107− let mod = nameModule name
139+ mod <- MaybeT $ return $ nameModule_maybe name
108− let unitId = moduleUnitId mod
109− pkgConfig <- MaybeT $ pure $ lookupPackageConfig unitId pkgState
110− hiePath <- MaybeT $ liftIO $ optLocateHieFile optPkgLocationOpts pkgConfig mod
111− hieFile <- MaybeT $ getHieFile hiePath
140+ (hieFile, srcPath) <- MaybeT $ getHieFile mod
112− avail <- MaybeT $ pure $ listToMaybe (filterAvails (eqName name) $ hie_exports hieFile)
141+ avail <- MaybeT $ pure $ find (eqName name . snd) $ hieExportNames hieFile
113− srcPath <- MaybeT $ liftIO $ optLocateSrcFile optPkgLocationOpts pkgConfig mod
114 -- The location will point to the source file used during compilation.
142 -- The location will point to the source file used during compilation.
115 -- This file might no longer exists and even if it does the path will be relative
143 -- This file might no longer exists and even if it does the path will be relative
116 -- to the compilation directory which we don’t know.
144 -- to the compilation directory which we don’t know.
117− let span = setFileName srcPath $ nameSrcSpan $ availName avail
145+ let span = setFileName srcPath $ fst avail
118 pure span
146 pure span
119 -- We ignore uniques and source spans and only compare the name and the module.
147 -- We ignore uniques and source spans and only compare the name and the module.
120 eqName :: Name -> Name -> Bool
148 eqName :: Name -> Name -> Bool
121− eqName n n' = nameOccName n == nameOccName n' && nameModule n == nameModule n'
149+ eqName n n' = nameOccName n == nameOccName n' && nameModule_maybe n == nameModule_maybe n'
122 setFileName f (RealSrcSpan span) = RealSrcSpan (span { srcSpanFile = mkFastString f })
150 setFileName f (RealSrcSpan span) = RealSrcSpan (span { srcSpanFile = mkFastString f })
123 setFileName _ span@(UnhelpfulSpan _) = span
151 setFileName _ span@(UnhelpfulSpan _) = span
124
152
153+-- | Filter out spans which do not enclose a given point
125spansAtPoint :: Position -> [SpanInfo] -> [SpanInfo]
154spansAtPoint :: Position -> [SpanInfo] -> [SpanInfo]
126spansAtPoint pos = filter atp where
155spansAtPoint pos = filter atp where
127− line = _line pos + 1
156+ line = _line pos
128− cha = _character pos + 1
157+ cha = _character pos
158+ atp SpanInfo{..} =
159+ startsBeforePosition && endsAfterPosition
160+ where
129− atp SpanInfo{..} = spaninfoStartLine <= line
161+ startLineCmp = compare spaninfoStartLine line
130− && spaninfoEndLine >= line
162+ endLineCmp = compare spaninfoEndLine line
163+
131− && spaninfoStartCol <= cha
164+ startsBeforePosition = startLineCmp == LT || (startLineCmp == EQ && spaninfoStartCol <= cha)
165+ -- The end col points to the column after the
166+ -- last character so we use > instead of >=
132− && spaninfoEndCol >= cha
167+ endsAfterPosition = endLineCmp == GT || (endLineCmp == EQ && spaninfoEndCol > cha)
133
168
134showName :: Outputable a => a -> T.Text
169showName :: Outputable a => a -> T.Text
135showName = T.pack . prettyprint
170showName = T.pack . prettyprint
src/Development/IDE/Spans/Calculate.hs+183−72
@@ -1,162 +1,273 @@
1−-- Copyright (c) 2019 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
1+-- Copyright (c) 2019 The DAML Authors. All rights reserved.
2-- SPDX-License-Identifier: Apache-2.0
2-- SPDX-License-Identifier: Apache-2.0
3
3
4-- ORIGINALLY COPIED FROM https://github.com/commercialhaskell/intero
4-- ORIGINALLY COPIED FROM https://github.com/commercialhaskell/intero
5
5
6+{-# LANGUAGE CPP #-}
6{-# LANGUAGE RankNTypes #-}
7{-# LANGUAGE RankNTypes #-}
8+#include "ghc-api-version.h"
7
9
8-- | Get information on modules, identifiers, etc.
10-- | Get information on modules, identifiers, etc.
9
11
10−module Development.IDE.Spans.Calculate(getSpanInfo,listifyAllSpans) where
12+module Development.IDE.Spans.Calculate(getSrcSpanInfos) where
11
13
12import ConLike
14import ConLike
13import Control.Monad
15import Control.Monad
14import qualified CoreUtils
16import qualified CoreUtils
15−import Data.Data
16−import qualified Data.Generics
17import Data.List
17import Data.List
18import Data.Maybe
18import Data.Maybe
19import DataCon
19import DataCon
20import Desugar
20import Desugar
21import GHC
21import GHC
22import GhcMonad
22import GhcMonad
23+import HscTypes
23import FastString (mkFastString)
24import FastString (mkFastString)
25+import OccName
24import Development.IDE.Types.Location
26import Development.IDE.Types.Location
25import Development.IDE.Spans.Type
27import Development.IDE.Spans.Type
28+#ifdef GHC_LIB
26import Development.IDE.GHC.Error (zeroSpan)
29import Development.IDE.GHC.Error (zeroSpan)
30+#else
31+import Development.IDE.GHC.Error (zeroSpan, catchSrcErrors)
32+#endif
27import Prelude hiding (mod)
33import Prelude hiding (mod)
28import TcHsSyn
34import TcHsSyn
29import Var
35import Var
36+import Development.IDE.Core.Compile
37+import qualified Development.IDE.GHC.Compat as Compat
38+import Development.IDE.GHC.Util
39+import Development.IDE.Spans.Common
40+import Development.IDE.Spans.Documentation
41+
42+-- A lot of things gained an extra X argument in GHC 8.6, which we mostly ignore
43+-- this U ignores that arg in 8.6, but is hidden in 8.4
44+#if MIN_GHC_API_VERSION(8,6,0)
45+#define U _
46+#else
47+#define U
48+#endif
49+
50+-- | Get source span info, used for e.g. AtPoint and Goto Definition.
51+getSrcSpanInfos
52+ :: HscEnv
53+ -> [(Located ModuleName, Maybe NormalizedFilePath)] -- ^ Dependencies in topological order
54+ -> TcModuleResult
55+ -> [ParsedModule] -- ^ Dependencies parsed, optional
56+ -> [ModIface] -- ^ Dependencies module interfaces, required
57+ -> IO SpansInfo
58+getSrcSpanInfos env imports tc parsedDeps deps =
59+ evalGhcEnv env $
60+ getSpanInfo imports (tmrModule tc) parsedDeps deps
30
61
31-- | Get ALL source spans in the module.
62-- | Get ALL source spans in the module.
32getSpanInfo :: GhcMonad m
63getSpanInfo :: GhcMonad m
33 => [(Located ModuleName, Maybe NormalizedFilePath)] -- ^ imports
64 => [(Located ModuleName, Maybe NormalizedFilePath)] -- ^ imports
34 -> TypecheckedModule
65 -> TypecheckedModule
35− -> m [SpanInfo]
66+ -> [ParsedModule]
67+ -> [ModIface]
68+ -> m SpansInfo
36−getSpanInfo mods tcm =
69+getSpanInfo mods tcm@TypecheckedModule{..} parsedDeps deps =
37− do let tcs = tm_typechecked_source tcm
70+ do let tcs = tm_typechecked_source
38 bs = listifyAllSpans tcs :: [LHsBind GhcTc]
71 bs = listifyAllSpans tcs :: [LHsBind GhcTc]
39 es = listifyAllSpans tcs :: [LHsExpr GhcTc]
72 es = listifyAllSpans tcs :: [LHsExpr GhcTc]
40 ps = listifyAllSpans' tcs :: [Pat GhcTc]
73 ps = listifyAllSpans' tcs :: [Pat GhcTc]
41− ts = listifyAllSpans $ tm_renamed_source tcm :: [LHsType GhcRn]
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+
42− bts <- mapM (getTypeLHsBind tcm) bs -- binds
83+ bts <- mapM (getTypeLHsBind allModules funBinds) bs -- binds
43− ets <- mapM (getTypeLHsExpr tcm) es -- expressions
84+ ets <- mapM (getTypeLHsExpr allModules) es -- expressions
44− pts <- mapM (getTypeLPat tcm) ps -- patterns
85+ pts <- mapM (getTypeLPat allModules) ps -- patterns
45− tts <- mapM (getLHsType tcm) ts -- types
86+ tts <- mapM (getLHsType allModules) ts -- types
46 let imports = importInfo mods
87 let imports = importInfo mods
47 let exports = getExports tcm
88 let exports = getExports tcm
48− let exprs = exports ++ imports ++ concat bts ++ concat tts ++ catMaybes (ets ++ pts)
89+ let exprs = addEmptyInfo exports ++ addEmptyInfo imports ++ concat bts ++ concat tts ++ catMaybes (ets ++ pts)
90+ let constraints = map constraintToInfo (concatMap getConstraintsLHsBind bs)
49− return (mapMaybe toSpanInfo (sortBy cmp exprs))
91+ return $ SpansInfo (mapMaybe toSpanInfo (sortBy cmp exprs))
92+ (mapMaybe toSpanInfo (sortBy cmp constraints))
50− where cmp (_,a,_) (_,b,_)
93+ where cmp (_,a,_,_) (_,b,_,_)
51 | a `isSubspanOf` b = LT
94 | a `isSubspanOf` b = LT
52 | b `isSubspanOf` a = GT
95 | b `isSubspanOf` a = GT
53− | otherwise = EQ
96+ | otherwise = compare (srcSpanStart a) (srcSpanStart b)
97+
98+ addEmptyInfo = map (\(a,b) -> (a,b,Nothing,emptySpanDoc))
99+ constraintToInfo (sp, ty) = (SpanS sp, sp, Just ty, emptySpanDoc)
100+
101+-- | The locations in the typechecked module are slightly messed up in some cases (e.g. HsMatchContext always
102+-- points to the first match) whereas the parsed module has the correct locations.
103+-- Therefore we build up a map from OccName to the corresponding definition in the parsed module
104+-- to lookup precise locations for things like multi-clause function definitions.
105+--
106+-- For now this only contains FunBinds.
107+funBindMap :: ParsedModule -> OccEnv (HsBind GhcPs)
108+funBindMap pm = mkOccEnv $ [ (occName $ unLoc f, bnd) | L _ (Compat.ValD bnd@FunBind{fun_id = f}) <- hsmodDecls $ unLoc $ pm_parsed_source pm ]
54
109
55−getExports :: TypecheckedModule -> [(SpanSource, SrcSpan, Maybe Type)]
110+getExports :: TypecheckedModule -> [(SpanSource, SrcSpan)]
56getExports m
111getExports m
57 | Just (_, _, Just exports, _) <- renamedSource m =
112 | Just (_, _, Just exports, _) <- renamedSource m =
58− [ (Named $ unLoc n, getLoc n, Nothing)
113+ [ (Named $ unLoc n, getLoc n)
59 | (e, _) <- exports
114 | (e, _) <- exports
60 , n <- ieLNames $ unLoc e
115 , n <- ieLNames $ unLoc e
61 ]
116 ]
62getExports _ = []
117getExports _ = []
63
118
64−-- | Variant of GHC’s ieNames that produces LIdP instead of IdP
119+-- | Variant of GHC's ieNames that produces LIdP instead of IdP
65−ieLNames :: IE pass -> [LIdP pass]
120+ieLNames :: IE pass -> [Located (IdP pass)]
66−ieLNames (IEVar _ n ) = [ieLWrappedName n]
121+ieLNames (IEVar U n ) = [ieLWrappedName n]
67−ieLNames (IEThingAbs _ n ) = [ieLWrappedName n]
122+ieLNames (IEThingAbs U n ) = [ieLWrappedName n]
68−ieLNames (IEThingAll _ n ) = [ieLWrappedName n]
123+ieLNames (IEThingAll U n ) = [ieLWrappedName n]
69−ieLNames (IEThingWith _ n _ ns _) = ieLWrappedName n : map ieLWrappedName ns
124+ieLNames (IEThingWith U n _ ns _) = ieLWrappedName n : map ieLWrappedName ns
70ieLNames _ = []
125ieLNames _ = []
71
126
72-- | Get the name and type of a binding.
127-- | Get the name and type of a binding.
73getTypeLHsBind :: (GhcMonad m)
128getTypeLHsBind :: (GhcMonad m)
74− => TypecheckedModule
129+ => [ParsedModule]
130+ -> OccEnv (HsBind GhcPs)
75 -> LHsBind GhcTc
131 -> LHsBind GhcTc
76− -> m [(SpanSource, SrcSpan, Maybe Type)]
132+ -> m [(SpanSource, SrcSpan, Maybe Type, SpanDoc)]
77−getTypeLHsBind _ (L _spn FunBind{fun_id = pid,fun_matches = MG _ _ _typ}) =
133+getTypeLHsBind deps funBinds (L _spn FunBind{fun_id = pid})
134+ | Just FunBind {fun_matches = MG{mg_alts=L _ matches}} <- lookupOccEnv funBinds (occName $ unLoc pid) = do
135+ let name = getName (unLoc pid)
136+ docs <- getDocumentationTryGhc deps name
137+ return [(Named name, getLoc mc_fun, Just (varType (unLoc pid)), docs) | match <- matches, FunRhs{mc_fun = mc_fun} <- [m_ctxt $ unLoc match] ]
138+-- 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
140+ let name = getName (unLoc pid)
141+ docs <- getDocumentationTryGhc deps name
78− return [(Named $ getName (unLoc pid), getLoc pid, Just (varType (unLoc pid)))]
142+ return [(Named name, getLoc pid, Just (varType (unLoc pid)), docs)]
79−getTypeLHsBind _ _ = return []
143+getTypeLHsBind _ _ _ = return []
144+
145+-- | Get information about constraints
146+getConstraintsLHsBind :: LHsBind GhcTc
147+ -> [(SrcSpan, Type)]
148+getConstraintsLHsBind (L spn AbsBinds { abs_ev_vars = vars })
149+ = map (\v -> (spn, varType v)) vars
150+getConstraintsLHsBind _ = []
80
151
81-- | Get the name and type of an expression.
152-- | Get the name and type of an expression.
82getTypeLHsExpr :: (GhcMonad m)
153getTypeLHsExpr :: (GhcMonad m)
83− => TypecheckedModule
154+ => [ParsedModule]
84 -> LHsExpr GhcTc
155 -> LHsExpr GhcTc
85− -> m (Maybe (SpanSource, SrcSpan, Maybe Type))
156+ -> m (Maybe (SpanSource, SrcSpan, Maybe Type, SpanDoc))
86−getTypeLHsExpr _ e = do
157+getTypeLHsExpr deps e = do
87 hs_env <- getSession
158 hs_env <- getSession
88 (_, mbe) <- liftIO (deSugarExpr hs_env e)
159 (_, mbe) <- liftIO (deSugarExpr hs_env e)
89− return $
90− case mbe of
160+ case mbe of
91− Just expr ->
161+ Just expr -> do
162+ let ss = getSpanSource (unLoc e)
163+ docs <- case ss of
164+ Named n -> getDocumentationTryGhc deps n
165+ _ -> return emptySpanDoc
92− Just (getSpanSource (unLoc e), getLoc e, Just (CoreUtils.exprType expr))
166+ return $ Just (ss, getLoc e, Just (CoreUtils.exprType expr), docs)
93− Nothing -> Nothing
167+ Nothing -> return Nothing
94 where
168 where
95 getSpanSource :: HsExpr GhcTc -> SpanSource
169 getSpanSource :: HsExpr GhcTc -> SpanSource
170+ getSpanSource xpr | isLit xpr = Lit (showGhc xpr)
96− getSpanSource (HsVar _ (L _ i)) = Named (getName i)
171+ getSpanSource (HsVar U (L _ i)) = Named (getName i)
97− getSpanSource (HsConLikeOut _ (RealDataCon dc)) = Named (dataConName dc)
172+ getSpanSource (HsConLikeOut U (RealDataCon dc)) = Named (dataConName dc)
98 getSpanSource RecordCon {rcon_con_name} = Named (getName rcon_con_name)
173 getSpanSource RecordCon {rcon_con_name} = Named (getName rcon_con_name)
99− getSpanSource (HsWrap _ _ xpr) = getSpanSource xpr
174+ getSpanSource (HsWrap U _ xpr) = getSpanSource xpr
100− getSpanSource (HsPar _ xpr) = getSpanSource (unLoc xpr)
175+ getSpanSource (HsPar U xpr) = getSpanSource (unLoc xpr)
101− getSpanSource _ = NoSource
176+ getSpanSource _ = NoSource
177+
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
102
207
103-- | Get the name and type of a pattern.
208-- | Get the name and type of a pattern.
104getTypeLPat :: (GhcMonad m)
209getTypeLPat :: (GhcMonad m)
105− => TypecheckedModule
210+ => [ParsedModule]
106 -> Pat GhcTc
211 -> Pat GhcTc
107− -> m (Maybe (SpanSource, SrcSpan, Maybe Type))
212+ -> m (Maybe (SpanSource, SrcSpan, Maybe Type, SpanDoc))
108−getTypeLPat _ pat =
213+getTypeLPat deps pat = do
109− let (src, spn) = getSpanSource pat in
214+ let (src, spn) = getSpanSource pat
215+ docs <- case src of
216+ Named n -> getDocumentationTryGhc deps n
217+ _ -> return emptySpanDoc
110− return $ Just (src, spn, Just (hsPatType pat))
218+ return $ Just (src, spn, Just (hsPatType pat), docs)
111 where
219 where
112 getSpanSource :: Pat GhcTc -> (SpanSource, SrcSpan)
220 getSpanSource :: Pat GhcTc -> (SpanSource, SrcSpan)
113− getSpanSource (VarPat _ (L spn vid)) = (Named (getName vid), spn)
221+ getSpanSource (VarPat U (L spn vid)) = (Named (getName vid), spn)
114 getSpanSource (ConPatOut (L spn (RealDataCon dc)) _ _ _ _ _ _) =
222 getSpanSource (ConPatOut (L spn (RealDataCon dc)) _ _ _ _ _ _) =
115 (Named (dataConName dc), spn)
223 (Named (dataConName dc), spn)
116 getSpanSource _ = (NoSource, noSrcSpan)
224 getSpanSource _ = (NoSource, noSrcSpan)
117
225
118getLHsType
226getLHsType
119 :: GhcMonad m
227 :: GhcMonad m
120− => TypecheckedModule
228+ => [ParsedModule]
121 -> LHsType GhcRn
229 -> LHsType GhcRn
122− -> m [(SpanSource, SrcSpan, Maybe Type)]
230+ -> m [(SpanSource, SrcSpan, Maybe Type, SpanDoc)]
123−getLHsType _ (L spn (HsTyVar _ _ v)) = pure [(Named $ unLoc v, spn, Nothing)]
231+getLHsType deps (L spn (HsTyVar U _ v)) = do
232+ let n = unLoc v
233+ docs <- getDocumentationTryGhc deps n
234+#ifdef GHC_LIB
235+ let ty = Right Nothing
236+#else
237+ ty <- catchSrcErrors "completion" $ do
238+ name' <- lookupName n
239+ return $ name' >>= safeTyThingType
240+#endif
241+ let ty' = case ty of
242+ Right (Just x) -> Just x
243+ _ -> Nothing
244+ pure [(Named n, spn, ty', docs)]
124getLHsType _ _ = pure []
245getLHsType _ _ = pure []
125
246
126importInfo :: [(Located ModuleName, Maybe NormalizedFilePath)]
247importInfo :: [(Located ModuleName, Maybe NormalizedFilePath)]
127− -> [(SpanSource, SrcSpan, Maybe Type)]
248+ -> [(SpanSource, SrcSpan)]
128importInfo = mapMaybe (uncurry wrk) where
249importInfo = mapMaybe (uncurry wrk) where
129− wrk :: Located ModuleName -> Maybe NormalizedFilePath -> Maybe (SpanSource, SrcSpan, Maybe Type)
250+ wrk :: Located ModuleName -> Maybe NormalizedFilePath -> Maybe (SpanSource, SrcSpan)
130 wrk modName = \case
251 wrk modName = \case
131 Nothing -> Nothing
252 Nothing -> Nothing
132− Just fp -> Just (fpToSpanSource $ fromNormalizedFilePath fp, getLoc modName, Nothing)
253+ Just fp -> Just (fpToSpanSource $ fromNormalizedFilePath fp, getLoc modName)
133
254
134 -- TODO make this point to the module name
255 -- TODO make this point to the module name
135 fpToSpanSource :: FilePath -> SpanSource
256 fpToSpanSource :: FilePath -> SpanSource
136 fpToSpanSource fp = SpanS $ RealSrcSpan $ zeroSpan $ mkFastString fp
257 fpToSpanSource fp = SpanS $ RealSrcSpan $ zeroSpan $ mkFastString fp
137
258
138−-- | Get ALL source spans in the source.
139−listifyAllSpans :: (Typeable a, Data m) => m -> [Located a]
140−listifyAllSpans tcs =
141− Data.Generics.listify p tcs
142− where p (L spn _) = isGoodSrcSpan spn
143−-- This is a version of `listifyAllSpans` specialized on picking out
144−-- patterns. It comes about since GHC now defines `type LPat p = Pat
145−-- p` (no top-level locations).
146−listifyAllSpans' :: Typeable a
147− => TypecheckedSource -> [Pat a]
148−listifyAllSpans' tcs = Data.Generics.listify (const True) tcs
149−
150−
151-- | Pretty print the types into a 'SpanInfo'.
259-- | Pretty print the types into a 'SpanInfo'.
152−toSpanInfo :: (SpanSource, SrcSpan, Maybe Type) -> Maybe SpanInfo
260+toSpanInfo :: (SpanSource, SrcSpan, Maybe Type, SpanDoc) -> Maybe SpanInfo
153−toSpanInfo (name,mspan,typ) =
261+toSpanInfo (name,mspan,typ,docs) =
154 case mspan of
262 case mspan of
155 RealSrcSpan spn ->
263 RealSrcSpan spn ->
264+ -- GHC’s line and column numbers are 1-based while LSP’s line and column
265+ -- numbers are 0-based.
156− Just (SpanInfo (srcSpanStartLine spn)
266+ Just (SpanInfo (srcSpanStartLine spn - 1)
157 (srcSpanStartCol spn - 1)
267 (srcSpanStartCol spn - 1)
158− (srcSpanEndLine spn)
268+ (srcSpanEndLine spn - 1)
159 (srcSpanEndCol spn - 1)
269 (srcSpanEndCol spn - 1)
160 typ
270 typ
161− name)
271+ name
272+ docs)
162 _ -> Nothing
273 _ -> Nothing
src/Development/IDE/Spans/Common.hsnew file+162
@@ -0,0 +1,162 @@
1+{-# LANGUAGE CPP #-}
2+#include "ghc-api-version.h"
3+
4+module Development.IDE.Spans.Common (
5+ showGhc
6+, listifyAllSpans
7+, listifyAllSpans'
8+, safeTyThingId
9+#ifndef GHC_LIB
10+, safeTyThingType
11+#endif
12+, SpanDoc(..)
13+, emptySpanDoc
14+, spanDocToMarkdown
15+, spanDocToMarkdownForTest
16+) where
17+
18+import Data.Data
19+import qualified Data.Generics
20+import qualified Data.Text as T
21+import Data.List.Extra
22+
23+import GHC
24+import Outputable
25+import DynFlags
26+import ConLike
27+import DataCon
28+#ifndef GHC_LIB
29+import Var
30+#endif
31+
32+import qualified Documentation.Haddock.Parser as H
33+import qualified Documentation.Haddock.Types as H
34+
35+showGhc :: Outputable a => a -> String
36+showGhc = showPpr unsafeGlobalDynFlags
37+
38+-- | Get ALL source spans in the source.
39+listifyAllSpans :: (Typeable a, Data m) => m -> [Located a]
40+listifyAllSpans tcs =
41+ Data.Generics.listify p tcs
42+ where p (L spn _) = isGoodSrcSpan spn
43+-- This is a version of `listifyAllSpans` specialized on picking out
44+-- patterns. It comes about since GHC now defines `type LPat p = Pat
45+-- p` (no top-level locations).
46+listifyAllSpans' :: Typeable a
47+ => TypecheckedSource -> [Pat a]
48+listifyAllSpans' tcs = Data.Generics.listify (const True) tcs
49+
50+#ifndef GHC_LIB
51+-- From haskell-ide-engine/src/Haskell/Ide/Engine/Support/HieExtras.hs
52+safeTyThingType :: TyThing -> Maybe Type
53+safeTyThingType thing
54+ | Just i <- safeTyThingId thing = Just (varType i)
55+safeTyThingType (ATyCon tycon) = Just (tyConKind tycon)
56+safeTyThingType _ = Nothing
57+#endif
58+
59+safeTyThingId :: TyThing -> Maybe Id
60+safeTyThingId (AnId i) = Just i
61+safeTyThingId (AConLike (RealDataCon dc)) = Just $ dataConWrapId dc
62+safeTyThingId _ = Nothing
63+
64+-- Possible documentation for an element in the code
65+data SpanDoc
66+ = SpanDocString HsDocString
67+ | SpanDocText [T.Text]
68+ deriving Show
69+
70+emptySpanDoc :: SpanDoc
71+emptySpanDoc = SpanDocText []
72+
73+spanDocToMarkdown :: SpanDoc -> [T.Text]
74+#if MIN_GHC_API_VERSION(8,6,0)
75+spanDocToMarkdown (SpanDocString docs)
76+ = [T.pack $ haddockToMarkdown $ H.toRegular $ H._doc $ H.parseParas Nothing $ unpackHDS docs]
77+#else
78+spanDocToMarkdown (SpanDocString _)
79+ = []
80+#endif
81+spanDocToMarkdown (SpanDocText txt) = txt
82+
83+spanDocToMarkdownForTest :: String -> String
84+spanDocToMarkdownForTest
85+ = haddockToMarkdown . H.toRegular . H._doc . H.parseParas Nothing
86+
87+-- Simple (and a bit hacky) conversion from Haddock markup to Markdown
88+haddockToMarkdown
89+ :: H.DocH String String -> String
90+
91+haddockToMarkdown H.DocEmpty
92+ = ""
93+haddockToMarkdown (H.DocAppend d1 d2)
94+ = haddockToMarkdown d1 ++ " " ++ haddockToMarkdown d2
95+haddockToMarkdown (H.DocString s)
96+ = s
97+haddockToMarkdown (H.DocParagraph p)
98+ = "\n\n" ++ haddockToMarkdown p
99+haddockToMarkdown (H.DocIdentifier i)
100+ = "`" ++ i ++ "`"
101+haddockToMarkdown (H.DocIdentifierUnchecked i)
102+ = "`" ++ i ++ "`"
103+haddockToMarkdown (H.DocModule i)
104+ = "`" ++ i ++ "`"
105+haddockToMarkdown (H.DocWarning w)
106+ = haddockToMarkdown w
107+haddockToMarkdown (H.DocEmphasis d)
108+ = "*" ++ haddockToMarkdown d ++ "*"
109+haddockToMarkdown (H.DocBold d)
110+ = "**" ++ haddockToMarkdown d ++ "**"
111+haddockToMarkdown (H.DocMonospaced d)
112+ = "`" ++ escapeBackticks (haddockToMarkdown d) ++ "`"
113+ where
114+ escapeBackticks "" = ""
115+ escapeBackticks ('`':ss) = '\\':'`':escapeBackticks ss
116+ escapeBackticks (s :ss) = s:escapeBackticks ss
117+haddockToMarkdown (H.DocCodeBlock d)
118+ = "\n```haskell\n" ++ haddockToMarkdown d ++ "\n```\n"
119+haddockToMarkdown (H.DocExamples es)
120+ = "\n```haskell\n" ++ unlines (map exampleToMarkdown es) ++ "\n```\n"
121+ where
122+ exampleToMarkdown (H.Example expr result)
123+ = ">>> " ++ expr ++ "\n" ++ unlines result
124+haddockToMarkdown (H.DocHyperlink (H.Hyperlink url Nothing))
125+ = "<" ++ url ++ ">"
126+haddockToMarkdown (H.DocHyperlink (H.Hyperlink url (Just label)))
127+ = "[" ++ haddockToMarkdown label ++ "](" ++ url ++ ")"
128+haddockToMarkdown (H.DocPic (H.Picture url Nothing))
129+ = "![](" ++ url ++ ")"
130+haddockToMarkdown (H.DocPic (H.Picture url (Just label)))
131+ = "![" ++ label ++ "](" ++ url ++ ")"
132+haddockToMarkdown (H.DocAName aname)
133+ = "[" ++ aname ++ "]:"
134+haddockToMarkdown (H.DocHeader (H.Header level title))
135+ = replicate level '#' ++ " " ++ haddockToMarkdown title
136+
137+haddockToMarkdown (H.DocUnorderedList things)
138+ = '\n' : (unlines $ map (("+ " ++) . trimStart . splitForList . haddockToMarkdown) things)
139+haddockToMarkdown (H.DocOrderedList things)
140+ = '\n' : (unlines $ map (("1. " ++) . trimStart . splitForList . haddockToMarkdown) things)
141+haddockToMarkdown (H.DocDefList things)
142+ = '\n' : (unlines $ map (\(term, defn) -> "+ **" ++ haddockToMarkdown term ++ "**: " ++ haddockToMarkdown defn) things)
143+
144+-- we cannot render math by default
145+haddockToMarkdown (H.DocMathInline _)
146+ = "*cannot render inline math formula*"
147+haddockToMarkdown (H.DocMathDisplay _)
148+ = "\n\n*cannot render display math formula*\n\n"
149+
150+-- TODO: render tables
151+haddockToMarkdown (H.DocTable _t)
152+ = "\n\n*tables are not yet supported*\n\n"
153+
154+-- things I don't really know how to handle
155+haddockToMarkdown (H.DocProperty _)
156+ = "" -- 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
src/Development/IDE/Spans/Documentation.hs+47−19
@@ -1,8 +1,12 @@
1−-- Copyright (c) 2019 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
1+-- Copyright (c) 2019 The DAML Authors. All rights reserved.
2-- SPDX-License-Identifier: Apache-2.0
2-- SPDX-License-Identifier: Apache-2.0
3
3
4+{-# LANGUAGE CPP #-}
5+#include "ghc-api-version.h"
6+
4module Development.IDE.Spans.Documentation (
7module Development.IDE.Spans.Documentation (
5 getDocumentation
8 getDocumentation
9+ , getDocumentationTryGhc
6 ) where
10 ) where
7
11
8import Control.Monad
12import Control.Monad
@@ -10,16 +14,34 @@ import Data.List.Extra
10import qualified Data.Map as M
14import qualified Data.Map as M
11import Data.Maybe
15import Data.Maybe
12import qualified Data.Text as T
16import qualified Data.Text as T
17+import Development.IDE.GHC.Compat
13import Development.IDE.GHC.Error
18import Development.IDE.GHC.Error
14−import Development.IDE.Spans.Calculate
19+import Development.IDE.Spans.Common
15import FastString
20import FastString
16−import GHC
17import SrcLoc
21import SrcLoc
18
22
19
23
24+getDocumentationTryGhc
25+ :: GhcMonad m
26+ => [ParsedModule]
27+ -> Name
28+ -> m SpanDoc
29+-- getDocs goes through the GHCi codepaths which cause problems on ghc-lib.
30+-- See https://github.com/digital-asset/daml/issues/4152 for more details.
31+#if MIN_GHC_API_VERSION(8,6,0) && !defined(GHC_LIB)
32+getDocumentationTryGhc sources name = do
33+ res <- catchSrcErrors "docs" $ getDocs name
34+ case res of
35+ Right (Right (Just docs, _)) -> return $ SpanDocString docs
36+ _ -> return $ SpanDocText $ getDocumentation sources name
37+#else
38+getDocumentationTryGhc sources name = do
39+ return $ SpanDocText $ getDocumentation sources name
40+#endif
41+
20getDocumentation
42getDocumentation
21− :: Name -- ^ The name you want documentation for.
22− -> [TypecheckedModule] -- ^ All of the possible modules it could be defined in.
43+ :: [ParsedModule] -- ^ All of the possible modules it could be defined in.
44+ -> Name -- ^ The name you want documentation for.
23 -> [T.Text]
45 -> [T.Text]
24-- This finds any documentation between the name you want
46-- This finds any documentation between the name you want
25-- documentation for and the one before it. This is only an
47-- documentation for and the one before it. This is only an
@@ -28,17 +50,18 @@ getDocumentation
28-- may be edge cases where it is very wrong).
50-- may be edge cases where it is very wrong).
29-- TODO : Build a version of GHC exactprint to extract this information
51-- TODO : Build a version of GHC exactprint to extract this information
30-- more accurately.
52-- more accurately.
31−getDocumentation targetName tcs = fromMaybe [] $ do
53+getDocumentation sources targetName = fromMaybe [] $ do
32 -- Find the module the target is defined in.
54 -- Find the module the target is defined in.
33 targetNameSpan <- realSpan $ nameSrcSpan targetName
55 targetNameSpan <- realSpan $ nameSrcSpan targetName
34 tc <-
56 tc <-
35− listToMaybe
36− $ filter ((==) (Just $ srcSpanFile targetNameSpan) . annotationFileName)
57+ find ((==) (Just $ srcSpanFile targetNameSpan) . annotationFileName)
37− $ reverse tcs -- TODO : Is reversing the list here really neccessary?
58+ $ reverse sources -- TODO : Is reversing the list here really neccessary?
59+
38− -- Names bound by the module (we want to exclude non-"top-level"
60+ -- Top level names bound by the module
39− -- bindings but unfortunately we get all here).
61+ let bs = [ n | let L _ HsModule{hsmodDecls} = pm_parsed_source tc
40− let bs = mapMaybe name_of_bind
62+ , L _ (ValD hsbind) <- hsmodDecls
41− (listifyAllSpans (tm_typechecked_source tc) :: [LHsBind GhcTc])
63+ , Just n <- [name_of_bind hsbind]
64+ ]
42 -- Sort the names' source spans.
65 -- Sort the names' source spans.
43 let sortedSpans = sortedNameSpans bs
66 let sortedSpans = sortedNameSpans bs
44 -- Now go ahead and extract the docs.
67 -- Now go ahead and extract the docs.
@@ -60,16 +83,16 @@ getDocumentation targetName tcs = fromMaybe [] $ do
60 where
83 where
61 -- Get the name bound by a binding. We only concern ourselves with
84 -- Get the name bound by a binding. We only concern ourselves with
62 -- @FunBind@ (which covers functions and variables).
85 -- @FunBind@ (which covers functions and variables).
63− name_of_bind :: LHsBind GhcTc -> Maybe Name
86+ name_of_bind :: HsBind GhcPs -> Maybe (Located RdrName)
64− name_of_bind (L _ FunBind {fun_id}) = Just (getName (unLoc fun_id))
87+ name_of_bind FunBind {fun_id} = Just fun_id
65 name_of_bind _ = Nothing
88 name_of_bind _ = Nothing
66 -- Get source spans from names, discard unhelpful spans, remove
89 -- Get source spans from names, discard unhelpful spans, remove
67 -- duplicates and sort.
90 -- duplicates and sort.
68− sortedNameSpans :: [Name] -> [RealSrcSpan]
91+ sortedNameSpans :: [Located RdrName] -> [RealSrcSpan]
69− sortedNameSpans ls = nubSort (mapMaybe (realSpan . nameSrcSpan) ls)
92+ sortedNameSpans ls = nubSort (mapMaybe (realSpan . getLoc) ls)
70 isBetween target before after = before <= target && target <= after
93 isBetween target before after = before <= target && target <= after
71− ann = snd . pm_annotations . tm_parsed_module
94+ ann = snd . pm_annotations
72− annotationFileName :: TypecheckedModule -> Maybe FastString
95+ annotationFileName :: ParsedModule -> Maybe FastString
73 annotationFileName = fmap srcSpanFile . listToMaybe . realSpans . ann
96 annotationFileName = fmap srcSpanFile . listToMaybe . realSpans . ann
74 realSpans :: M.Map SrcSpan [Located a] -> [RealSrcSpan]
97 realSpans :: M.Map SrcSpan [Located a] -> [RealSrcSpan]
75 realSpans =
98 realSpans =
@@ -83,5 +106,10 @@ docHeaders :: [RealLocated AnnotationComment]
83docHeaders = mapMaybe (\(L _ x) -> wrk x)
106docHeaders = mapMaybe (\(L _ x) -> wrk x)
84 where
107 where
85 wrk = \case
108 wrk = \case
109+ -- When `Opt_Haddock` is enabled.
86 AnnDocCommentNext s -> Just $ T.pack s
110 AnnDocCommentNext s -> Just $ T.pack s
111+ -- When `Opt_KeepRawTokenStream` enabled.
112+ AnnLineComment s -> if "-- |" `isPrefixOf` s
113+ then Just $ T.pack s
114+ else Nothing
87 _ -> Nothing
115 _ -> Nothing
src/Development/IDE/Spans/Type.hs+26−10
@@ -1,4 +1,4 @@
1−-- Copyright (c) 2019 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
1+-- Copyright (c) 2019 The DAML Authors. All rights reserved.
2-- SPDX-License-Identifier: Apache-2.0
2-- SPDX-License-Identifier: Apache-2.0
3
3
4-- ORIGINALLY COPIED FROM https://github.com/commercialhaskell/intero
4-- ORIGINALLY COPIED FROM https://github.com/commercialhaskell/intero
@@ -6,38 +6,52 @@
6-- | Types used separate to GHCi vanilla.
6-- | Types used separate to GHCi vanilla.
7
7
8module Development.IDE.Spans.Type(
8module Development.IDE.Spans.Type(
9+ SpansInfo(..)
9− SpanInfo(..)
10+ , SpanInfo(..)
10 , SpanSource(..)
11 , SpanSource(..)
11 , getNameM
12 , getNameM
12 ) where
13 ) where
13
14
14import GHC
15import GHC
15import Control.DeepSeq
16import Control.DeepSeq
16−import Data.Maybe
17import OccName
17import OccName
18+import Development.IDE.GHC.Util
19+import Development.IDE.Spans.Common
18
20
21+data SpansInfo =
22+ SpansInfo { spansExprs :: [SpanInfo]
23+ , spansConstraints :: [SpanInfo] }
24+ deriving Show
25+
26+instance NFData SpansInfo where
27+ rnf (SpansInfo e c) = liftRnf rnf e `seq` liftRnf rnf c
19
28
20-- | Type of some span of source code. Most of these fields are
29-- | Type of some span of source code. Most of these fields are
21-- unboxed but Haddock doesn't show that.
30-- unboxed but Haddock doesn't show that.
22data SpanInfo =
31data SpanInfo =
23 SpanInfo {spaninfoStartLine :: {-# UNPACK #-} !Int
32 SpanInfo {spaninfoStartLine :: {-# UNPACK #-} !Int
24− -- ^ Start line of the span.
33+ -- ^ Start line of the span, zero-based.
25 ,spaninfoStartCol :: {-# UNPACK #-} !Int
34 ,spaninfoStartCol :: {-# UNPACK #-} !Int
26− -- ^ Start column of the span.
35+ -- ^ Start column of the span, zero-based.
27 ,spaninfoEndLine :: {-# UNPACK #-} !Int
36 ,spaninfoEndLine :: {-# UNPACK #-} !Int
28− -- ^ End line of the span (absolute).
37+ -- ^ End line of the span (absolute), zero-based.
29 ,spaninfoEndCol :: {-# UNPACK #-} !Int
38 ,spaninfoEndCol :: {-# UNPACK #-} !Int
30− -- ^ End column of the span (absolute).
39+ -- ^ End column of the span (absolute), zero-based.
31 ,spaninfoType :: !(Maybe Type)
40 ,spaninfoType :: !(Maybe Type)
32− -- ^ A pretty-printed representation fo the type.
41+ -- ^ A pretty-printed representation for the type.
33 ,spaninfoSource :: !SpanSource
42 ,spaninfoSource :: !SpanSource
34 -- ^ The actutal 'Name' associated with the span, if
43 -- ^ The actutal 'Name' associated with the span, if
35 -- any. This can be useful for accessing a variety of
44 -- any. This can be useful for accessing a variety of
36 -- information about the identifier such as module,
45 -- information about the identifier such as module,
37 -- locality, definition location, etc.
46 -- locality, definition location, etc.
47+ ,spaninfoDocs :: !SpanDoc
48+ -- ^ Documentation for the element
38 }
49 }
39instance Show SpanInfo where
50instance Show SpanInfo where
40− show (SpanInfo sl sc el ec t n) = show [show sl, show sc, show el, show ec, show $ isJust t, show n]
51+ show (SpanInfo sl sc el ec t n docs) =
52+ unwords ["(SpanInfo", show sl, show sc, show el, show ec
53+ , show $ maybe "NoType" prettyPrint t, "(" <> show n <> "))"
54+ , "docs(" <> show docs <> ")"]
41
55
42instance NFData SpanInfo where
56instance NFData SpanInfo where
43 rnf = rwhnf
57 rnf = rwhnf
@@ -46,6 +60,7 @@ instance NFData SpanInfo where
46-- we don't always get a name out so sometimes manually annotating source is more appropriate
60-- we don't always get a name out so sometimes manually annotating source is more appropriate
47data SpanSource = Named Name
61data SpanSource = Named Name
48 | SpanS SrcSpan
62 | SpanS SrcSpan
63+ | Lit String
49 | NoSource
64 | NoSource
50 deriving (Eq)
65 deriving (Eq)
51
66
@@ -53,9 +68,10 @@ instance Show SpanSource where
53 show = \case
68 show = \case
54 Named n -> "Named " ++ occNameString (occName n)
69 Named n -> "Named " ++ occNameString (occName n)
55 SpanS sp -> "Span " ++ show sp
70 SpanS sp -> "Span " ++ show sp
71+ Lit lit -> "Lit " ++ lit
56 NoSource -> "NoSource"
72 NoSource -> "NoSource"
57
73
58getNameM :: SpanSource -> Maybe Name
74getNameM :: SpanSource -> Maybe Name
59getNameM = \case
75getNameM = \case
60 Named name -> Just name
76 Named name -> Just name
61− _ -> Nothing
77+ _ -> Nothing
src/Development/IDE/Types/Diagnostics.hs+39−11
@@ -1,24 +1,26 @@
1−-- Copyright (c) 2019 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
1+-- Copyright (c) 2019 The DAML Authors. All rights reserved.
2-- SPDX-License-Identifier: Apache-2.0
2-- SPDX-License-Identifier: Apache-2.0
3
3
4−{-# LANGUAGE OverloadedStrings #-}
5
4
6module Development.IDE.Types.Diagnostics (
5module Development.IDE.Types.Diagnostics (
7 LSP.Diagnostic(..),
6 LSP.Diagnostic(..),
7+ ShowDiagnostic(..),
8 FileDiagnostic,
8 FileDiagnostic,
9+ IdeResult,
9 LSP.DiagnosticSeverity(..),
10 LSP.DiagnosticSeverity(..),
10 DiagnosticStore,
11 DiagnosticStore,
11 List(..),
12 List(..),
12 ideErrorText,
13 ideErrorText,
14+ ideErrorWithSource,
13 showDiagnostics,
15 showDiagnostics,
14 showDiagnosticsColored,
16 showDiagnosticsColored,
15 ) where
17 ) where
16
18
19+import Control.DeepSeq
17import Data.Maybe as Maybe
20import Data.Maybe as Maybe
18import qualified Data.Text as T
21import qualified Data.Text as T
19import Data.Text.Prettyprint.Doc
22import Data.Text.Prettyprint.Doc
20−import qualified Language.Haskell.LSP.Types as LSP
21−import Language.Haskell.LSP.Types as LSP (
23+import Language.Haskell.LSP.Types as LSP (DiagnosticSource,
22 DiagnosticSeverity(..)
24 DiagnosticSeverity(..)
23 , Diagnostic(..)
25 , Diagnostic(..)
24 , List(..)
26 , List(..)
@@ -30,17 +32,43 @@ import Data.Text.Prettyprint.Doc.Render.Terminal (Color(..), color)
30
32
31import Development.IDE.Types.Location
33import Development.IDE.Types.Location
32
34
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)
33
38
34ideErrorText :: NormalizedFilePath -> T.Text -> FileDiagnostic
39ideErrorText :: 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)
35−ideErrorText fp msg = (fp, LSP.Diagnostic {
48+ideErrorWithSource source sev fp msg = (fp, ShowDiag, LSP.Diagnostic {
36 _range = noRange,
49 _range = noRange,
37− _severity = Just LSP.DsError,
50+ _severity = sev,
38 _code = Nothing,
51 _code = Nothing,
39− _source = Just "compiler",
52+ _source = source,
40 _message = msg,
53 _message = msg,
41− _relatedInformation = Nothing
54+ _relatedInformation = Nothing,
55+ _tags = Nothing
42 })
56 })
43
57
58+-- | Defines whether a particular diagnostic should be reported
59+-- back to the user.
60+--
61+-- One important use case is "missing signature" code lenses,
62+-- for which we need to enable the corresponding warning during
63+-- type checking. However, we do not want to show the warning
64+-- unless the programmer asks for it (#261).
65+data ShowDiagnostic
66+ = ShowDiag -- ^ Report back to the user
67+ | HideDiag -- ^ Hide from user
68+ deriving (Eq, Ord, Show)
69+
70+instance NFData ShowDiagnostic where
71+ rnf = rwhnf
44
72
45-- | Human readable diagnostics for a specific file.
73-- | Human readable diagnostics for a specific file.
46--
74--
@@ -48,7 +76,7 @@ ideErrorText fp msg = (fp, LSP.Diagnostic {
48-- along with the related source location so that we can display the error
76-- along with the related source location so that we can display the error
49-- on either the console or in the IDE at the right source location.
77-- on either the console or in the IDE at the right source location.
50--
78--
51−type FileDiagnostic = (NormalizedFilePath, Diagnostic)
79+type FileDiagnostic = (NormalizedFilePath, ShowDiagnostic, Diagnostic)
52
80
53prettyRange :: Range -> Doc Terminal.AnsiStyle
81prettyRange :: Range -> Doc Terminal.AnsiStyle
54prettyRange Range{..} = f _start <> "-" <> f _end
82prettyRange Range{..} = f _start <> "-" <> f _end
@@ -68,9 +96,10 @@ prettyDiagnostics :: [FileDiagnostic] -> Doc Terminal.AnsiStyle
68prettyDiagnostics = vcat . map prettyDiagnostic
96prettyDiagnostics = vcat . map prettyDiagnostic
69
97
70prettyDiagnostic :: FileDiagnostic -> Doc Terminal.AnsiStyle
98prettyDiagnostic :: FileDiagnostic -> Doc Terminal.AnsiStyle
71−prettyDiagnostic (fp, LSP.Diagnostic{..}) =
99+prettyDiagnostic (fp, sh, LSP.Diagnostic{..}) =
72 vcat
100 vcat
73 [ slabel_ "File: " $ pretty (fromNormalizedFilePath fp)
101 [ slabel_ "File: " $ pretty (fromNormalizedFilePath fp)
102+ , slabel_ "Hidden: " $ if sh == ShowDiag then "no" else "yes"
74 , slabel_ "Range: " $ prettyRange _range
103 , slabel_ "Range: " $ prettyRange _range
75 , slabel_ "Source: " $ pretty _source
104 , slabel_ "Source: " $ pretty _source
76 , slabel_ "Severity:" $ pretty $ show sev
105 , slabel_ "Severity:" $ pretty $ show sev
@@ -81,7 +110,6 @@ prettyDiagnostic (fp, LSP.Diagnostic{..}) =
81 LSP.DsInfo -> annotate $ color Blue
110 LSP.DsInfo -> annotate $ color Blue
82 LSP.DsHint -> annotate $ color Magenta
111 LSP.DsHint -> annotate $ color Magenta
83 $ stringParagraphs _message
112 $ stringParagraphs _message
84− , slabel_ "Code:" $ pretty _code
85 ]
113 ]
86 where
114 where
87 sev = fromMaybe LSP.DsError _severity
115 sev = fromMaybe LSP.DsError _severity
src/Development/IDE/Types/Location.hs+71−60
@@ -1,7 +1,6 @@
1−-- Copyright (c) 2019 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
1+-- Copyright (c) 2019 The DAML Authors. All rights reserved.
2-- SPDX-License-Identifier: Apache-2.0
2-- SPDX-License-Identifier: Apache-2.0
3
3
4−{-# LANGUAGE OverloadedStrings #-}
5
4
6-- | Types and functions for working with source code locations.
5-- | Types and functions for working with source code locations.
7module Development.IDE.Types.Location
6module Development.IDE.Types.Location
@@ -11,83 +10,59 @@ module Development.IDE.Types.Location
11 , Position(..)
10 , Position(..)
12 , showPosition
11 , showPosition
13 , Range(..)
12 , Range(..)
14− , Uri(..)
13+ , LSP.Uri(..)
15− , NormalizedUri
14+ , LSP.NormalizedUri
16 , LSP.toNormalizedUri
15 , LSP.toNormalizedUri
17 , LSP.fromNormalizedUri
16 , LSP.fromNormalizedUri
18− , NormalizedFilePath
17+ , LSP.NormalizedFilePath
19 , fromUri
18 , fromUri
19+ , emptyFilePath
20+ , emptyPathUri
20− , toNormalizedFilePath
21+ , toNormalizedFilePath'
21− , fromNormalizedFilePath
22+ , LSP.fromNormalizedFilePath
22− , filePathToUri
23 , filePathToUri'
23 , filePathToUri'
24 , uriToFilePath'
24 , uriToFilePath'
25+ , readSrcSpan
25 ) where
26 ) where
26
27
28+import Control.Applicative
27import Language.Haskell.LSP.Types (Location(..), Range(..), Position(..))
29import Language.Haskell.LSP.Types (Location(..), Range(..), Position(..))
28−import Control.DeepSeq
30+import Control.Monad
29−import Data.Maybe as Maybe
31+import Data.Hashable (Hashable(hash))
30−import Data.Hashable
31import Data.String
32import Data.String
32−import System.FilePath
33+import FastString
33−import System.Info.Extra
34import qualified Language.Haskell.LSP.Types as LSP
34import qualified Language.Haskell.LSP.Types as LSP
35−import Language.Haskell.LSP.Types as LSP (
35+import SrcLoc as GHC
36− filePathToUri
36+import Text.ParserCombinators.ReadP as ReadP
37− , NormalizedUri(..)
37+import Data.Maybe (fromMaybe)
38− , Uri(..)
38+
39− , toNormalizedUri
40− , fromNormalizedUri
41− )
42−
43−
44−-- | Newtype wrapper around FilePath that always has normalized slashes.
45−newtype NormalizedFilePath = NormalizedFilePath FilePath
46− deriving (Eq, Ord, Show, Hashable, NFData)
47−
48−instance IsString NormalizedFilePath where
49− fromString = toNormalizedFilePath
50−
51−toNormalizedFilePath :: FilePath -> NormalizedFilePath
39+toNormalizedFilePath' :: FilePath -> LSP.NormalizedFilePath
52−toNormalizedFilePath "" = NormalizedFilePath ""
40+-- We want to keep empty paths instead of normalising them to "."
53−toNormalizedFilePath fp = NormalizedFilePath $ normalise' fp
41+toNormalizedFilePath' "" = emptyFilePath
54− where
42+toNormalizedFilePath' fp = LSP.toNormalizedFilePath fp
55− -- We do not use System.FilePath’s normalise here since that
43+
56− -- also normalises things like the case of the drive letter
44+emptyFilePath :: LSP.NormalizedFilePath
57− -- which NormalizedUri does not normalise so we get VFS lookup failures.
45+emptyFilePath = LSP.NormalizedFilePath emptyPathUri ""
58− normalise' :: FilePath -> FilePath
59− normalise' = oneSlash . map (\c -> if isPathSeparator c then pathSeparator else c)
60−
61− -- Allow double slashes as the very first element of the path for UNC drives on Windows
62− -- otherwise turn adjacent slashes into one. These slashes often arise from dodgy CPP
63− oneSlash :: FilePath -> FilePath
64− oneSlash (x:xs) | isWindows = x : f xs
65− oneSlash xs = f xs
66−
67− f (x:y:xs) | isPathSeparator x, isPathSeparator y = f (x:xs)
68− f (x:xs) = x : f xs
69− f [] = []
70−
71−
72−fromNormalizedFilePath :: NormalizedFilePath -> FilePath
73−fromNormalizedFilePath (NormalizedFilePath fp) = fp
74
46
75-- | We use an empty string as a filepath when we don’t have a file.
47-- | We use an empty string as a filepath when we don’t have a file.
76-- However, haskell-lsp doesn’t support that in uriToFilePath and given
48-- However, haskell-lsp doesn’t support that in uriToFilePath and given
77-- that it is not a valid filepath it does not make sense to upstream a fix.
49-- that it is not a valid filepath it does not make sense to upstream a fix.
78-- So we have our own wrapper here that supports empty filepaths.
50-- So we have our own wrapper here that supports empty filepaths.
79−uriToFilePath' :: Uri -> Maybe FilePath
51+uriToFilePath' :: LSP.Uri -> Maybe FilePath
80uriToFilePath' uri
52uriToFilePath' uri
81− | uri == filePathToUri "" = Just ""
53+ | uri == LSP.fromNormalizedUri emptyPathUri = Just ""
82 | otherwise = LSP.uriToFilePath uri
54 | otherwise = LSP.uriToFilePath uri
83
55
84−filePathToUri' :: NormalizedFilePath -> NormalizedUri
56+emptyPathUri :: LSP.NormalizedUri
85−filePathToUri' = toNormalizedUri . filePathToUri . fromNormalizedFilePath
57+emptyPathUri =
86−
58+ let s = "file://"
59+ in LSP.NormalizedUri (hash s) s
87
60
88−fromUri :: LSP.NormalizedUri -> NormalizedFilePath
61+filePathToUri' :: LSP.NormalizedFilePath -> LSP.NormalizedUri
89−fromUri = toNormalizedFilePath . fromMaybe noFilePath . uriToFilePath' . fromNormalizedUri
62+filePathToUri' = LSP.normalizedFilePathToUri
90
63
64+fromUri :: LSP.NormalizedUri -> LSP.NormalizedFilePath
65+fromUri = fromMaybe (toNormalizedFilePath' noFilePath) . LSP.uriToNormalizedFilePath
91
66
92noFilePath :: FilePath
67noFilePath :: FilePath
93noFilePath = "<unknown>"
68noFilePath = "<unknown>"
@@ -96,6 +71,42 @@ noFilePath = "<unknown>"
96noRange :: Range
71noRange :: Range
97noRange = Range (Position 0 0) (Position 100000 0)
72noRange = Range (Position 0 0) (Position 100000 0)
98
73
99−
100showPosition :: Position -> String
74showPosition :: Position -> String
101showPosition Position{..} = show (_line + 1) ++ ":" ++ show (_character + 1)
75showPosition Position{..} = show (_line + 1) ++ ":" ++ show (_character + 1)
76+
77+-- | Parser for the GHC output format
78+readSrcSpan :: ReadS SrcSpan
79+readSrcSpan = readP_to_S (singleLineSrcSpanP <|> multiLineSrcSpanP)
80+ where
81+ singleLineSrcSpanP, multiLineSrcSpanP :: ReadP SrcSpan
82+ singleLineSrcSpanP = do
83+ fp <- filePathP
84+ l <- readS_to_P reads <* char ':'
85+ c0 <- readS_to_P reads
86+ c1 <- (char '-' *> readS_to_P reads) <|> pure c0
87+ let from = mkSrcLoc fp l c0
88+ to = mkSrcLoc fp l c1
89+ return $ mkSrcSpan from to
90+
91+ multiLineSrcSpanP = do
92+ fp <- filePathP
93+ s <- parensP (srcLocP fp)
94+ void $ char '-'
95+ e <- parensP (srcLocP fp)
96+ return $ mkSrcSpan s e
97+
98+ parensP :: ReadP a -> ReadP a
99+ parensP = between (char '(') (char ')')
100+
101+ filePathP :: ReadP FastString
102+ filePathP = fromString <$> (readFilePath <* char ':') <|> pure ""
103+
104+ srcLocP :: FastString -> ReadP SrcLoc
105+ srcLocP fp = do
106+ l <- readS_to_P reads
107+ void $ char ','
108+ c <- readS_to_P reads
109+ return $ mkSrcLoc fp l c
110+
111+ readFilePath :: ReadP FilePath
112+ readFilePath = some ReadP.get
src/Development/IDE/Types/Logger.hs+41−15
@@ -1,4 +1,4 @@
1−-- Copyright (c) 2019 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
1+-- Copyright (c) 2019 The DAML Authors. All rights reserved.
2-- SPDX-License-Identifier: Apache-2.0
2-- SPDX-License-Identifier: Apache-2.0
3
3
4{-# LANGUAGE RankNTypes #-}
4{-# LANGUAGE RankNTypes #-}
@@ -6,23 +6,49 @@
6-- concrete choice of logging framework so users can plug in whatever
6-- concrete choice of logging framework so users can plug in whatever
7-- framework they want to.
7-- framework they want to.
8module Development.IDE.Types.Logger
8module Development.IDE.Types.Logger
9+ ( Priority(..)
9− ( Logger(..)
10+ , Logger(..)
10− , makeOneLogger
11+ , logError, logWarning, logInfo, logDebug, logTelemetry
11− , makeNopLogger
12+ , noLogging
12 ) where
13 ) where
13
14
14import qualified Data.Text as T
15import qualified Data.Text as T
15−import GHC.Stack
16
16
17−data Logger = Logger {
18− logSeriousError :: HasCallStack => T.Text -> IO ()
19− , logInfo :: HasCallStack => T.Text -> IO ()
20− , logDebug :: HasCallStack => T.Text -> IO ()
21− , logWarning :: HasCallStack => T.Text -> IO ()
22− }
23
17
24−makeNopLogger :: Logger
18+data Priority
25−makeNopLogger = makeOneLogger $ const $ pure ()
19+-- Don't change the ordering of this type or you will mess up the Ord
20+-- instance
21+ = Telemetry -- ^ Events that are useful for gathering user metrics.
22+ | Debug -- ^ Verbose debug logging.
23+ | Info -- ^ Useful information in case an error has to be understood.
24+ | Warning
25+ -- ^ These error messages should not occur in a expected usage, and
26+ -- should be investigated.
27+ | Error -- ^ Such log messages must never occur in expected usage.
28+ deriving (Eq, Show, Ord, Enum, Bounded)
26
29
27−makeOneLogger :: (HasCallStack => T.Text -> IO ()) -> Logger
30+
28−makeOneLogger x = Logger x x x x
31+-- | Note that this is logging actions _of the program_, not of the user.
32+-- You shouldn't call warning/error if the user has caused an error, only
33+-- if our code has gone wrong and is itself erroneous (e.g. we threw an exception).
34+data Logger = Logger {logPriority :: Priority -> T.Text -> IO ()}
35+
36+
37+logError :: Logger -> T.Text -> IO ()
38+logError x = logPriority x Error
39+
40+logWarning :: Logger -> T.Text -> IO ()
41+logWarning x = logPriority x Warning
42+
43+logInfo :: Logger -> T.Text -> IO ()
44+logInfo x = logPriority x Info
45+
46+logDebug :: Logger -> T.Text -> IO ()
47+logDebug x = logPriority x Debug
48+
49+logTelemetry :: Logger -> T.Text -> IO ()
50+logTelemetry x = logPriority x Telemetry
51+
52+
53+noLogging :: Logger
54+noLogging = Logger $ \_ _ -> return ()
src/Development/IDE/Types/Options.hs+85−13
@@ -1,4 +1,4 @@
1−-- Copyright (c) 2019 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
1+-- Copyright (c) 2019 The DAML Authors. All rights reserved.
2-- SPDX-License-Identifier: Apache-2.0
2-- SPDX-License-Identifier: Apache-2.0
3
3
4{-# LANGUAGE RankNTypes #-}
4{-# LANGUAGE RankNTypes #-}
@@ -6,41 +6,93 @@
6-- | Options
6-- | Options
7module Development.IDE.Types.Options
7module Development.IDE.Types.Options
8 ( IdeOptions(..)
8 ( IdeOptions(..)
9+ , IdePreprocessedSource(..)
10+ , IdeReportProgress(..)
11+ , IdeDefer(..)
12+ , clientSupportsProgress
9 , IdePkgLocationOptions(..)
13 , IdePkgLocationOptions(..)
10 , defaultIdeOptions
14 , defaultIdeOptions
15+ , IdeResult
11 ) where
16 ) where
12
17
13import Development.Shake
18import Development.Shake
19+import Development.IDE.GHC.Util
14import GHC hiding (parseModule, typecheckModule)
20import GHC hiding (parseModule, typecheckModule)
15import GhcPlugins as GHC hiding (fst3, (<>))
21import GhcPlugins as GHC hiding (fst3, (<>))
16−
22+import qualified Language.Haskell.LSP.Types.Capabilities as LSP
23+import qualified Data.Text as T
24+import Development.IDE.Types.Diagnostics
17
25
18data IdeOptions = IdeOptions
26data IdeOptions = IdeOptions
19− { optPreprocessor :: GHC.ParsedSource -> ([(GHC.SrcSpan, String)], GHC.ParsedSource)
27+ { optPreprocessor :: GHC.ParsedSource -> IdePreprocessedSource
28+ -- ^ Preprocessor to run over all parsed source trees, generating a list of warnings
29+ -- and a list of errors, along with a new parse tree.
20− , optGhcSession :: Action HscEnv
30+ , optGhcSession :: Action (FilePath -> Action (IdeResult HscEnvEq))
21− -- ^ Setup a GHC session using a given package state. If a `ParsedModule` is supplied,
31+ -- ^ Setup a GHC session for a given file, e.g. @Foo.hs@.
22− -- the import path should be setup for that module.
32+ -- For the same 'ComponentOptions' from hie-bios, the resulting function will be applied once per file.
33+ -- It is desirable that many files get the same 'HscEnvEq', so that more IDE features work.
23 , optPkgLocationOpts :: IdePkgLocationOptions
34 , optPkgLocationOpts :: IdePkgLocationOptions
24− , optWriteIface :: Bool
35+ -- ^ How to locate source and @.hie@ files given a module name.
25 , optExtensions :: [String]
36 , optExtensions :: [String]
37+ -- ^ File extensions to search for code, defaults to Haskell sources (including @.hs@)
26
38
27 , optThreads :: Int
39 , optThreads :: Int
40+ -- ^ Number of threads to use. Use 0 for number of threads on the machine.
41+ , optShakeFiles :: Maybe FilePath
42+ -- ^ Directory where the shake database should be stored. For ghcide this is always set to `Nothing` for now
43+ -- meaning we keep everything in memory but the daml CLI compiler uses this for incremental builds.
28 , optShakeProfiling :: Maybe FilePath
44 , optShakeProfiling :: Maybe FilePath
45+ -- ^ 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
48+ , optReportProgress :: IdeReportProgress
49+ -- ^ Whether to report progress during long operations.
29− , optLanguageSyntax :: String -- ^ the ```language to use
50+ , optLanguageSyntax :: String
51+ -- ^ the ```language to use
52+ , optNewColonConvention :: Bool
30− , optNewColonConvention :: Bool -- ^ whether to use new colon convention
53+ -- ^ 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.
57+ , optDefer :: IdeDefer
58+ -- ^ Whether to defer type errors, typed holes and out of scope
59+ -- variables. Deferral allows the IDE to continue to provide
60+ -- features such as diagnostics and go-to-definition, in
61+ -- situations in which they would become unavailable because of
62+ -- the presence of type errors, holes or unbound variables.
63+ }
64+
65+data IdePreprocessedSource = IdePreprocessedSource
66+ { preprocWarnings :: [(GHC.SrcSpan, String)]
67+ -- ^ Warnings emitted by the preprocessor.
68+ , preprocErrors :: [(GHC.SrcSpan, String)]
69+ -- ^ Errors emitted by the preprocessor.
70+ , preprocSource :: GHC.ParsedSource
71+ -- ^ New parse tree emitted by the preprocessor.
31 }
72 }
32
73
74+newtype IdeReportProgress = IdeReportProgress Bool
75+newtype IdeDefer = IdeDefer Bool
76+
77+clientSupportsProgress :: LSP.ClientCapabilities -> IdeReportProgress
78+clientSupportsProgress caps = IdeReportProgress $ Just True ==
79+ (LSP._workDoneProgress =<< LSP._window (caps :: LSP.ClientCapabilities))
80+
33−defaultIdeOptions :: Action HscEnv -> IdeOptions
81+defaultIdeOptions :: Action (FilePath -> Action (IdeResult HscEnvEq)) -> IdeOptions
34defaultIdeOptions session = IdeOptions
82defaultIdeOptions session = IdeOptions
35− {optPreprocessor = (,) []
83+ {optPreprocessor = IdePreprocessedSource [] []
36− ,optWriteIface = False
37 ,optGhcSession = session
84 ,optGhcSession = session
38− ,optExtensions = ["hs"]
85+ ,optExtensions = ["hs", "lhs"]
39 ,optPkgLocationOpts = defaultIdePkgLocationOptions
86 ,optPkgLocationOpts = defaultIdePkgLocationOptions
40 ,optThreads = 0
87 ,optThreads = 0
88+ ,optShakeFiles = Nothing
41 ,optShakeProfiling = Nothing
89 ,optShakeProfiling = Nothing
90+ ,optReportProgress = IdeReportProgress False
42 ,optLanguageSyntax = "haskell"
91 ,optLanguageSyntax = "haskell"
43 ,optNewColonConvention = False
92 ,optNewColonConvention = False
93+ ,optKeywords = haskellKeywords
94+ ,optDefer = IdeDefer True
95+ ,optTesting = False
44 }
96 }
45
97
46
98
@@ -58,3 +110,23 @@ data IdePkgLocationOptions = IdePkgLocationOptions
58defaultIdePkgLocationOptions :: IdePkgLocationOptions
110defaultIdePkgLocationOptions :: IdePkgLocationOptions
59defaultIdePkgLocationOptions = IdePkgLocationOptions f f
111defaultIdePkgLocationOptions = IdePkgLocationOptions f f
60 where f _ _ = return Nothing
112 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.yamlnew file+24
@@ -0,0 +1,24 @@
1+resolver: nightly-2019-09-16
2+packages:
3+- .
4+extra-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
9+- ghc-lib-parser-8.8.1
10+- ghc-lib-8.8.1
11+- fuzzy-0.1.0.0
12+- shake-0.18.5
13+- regex-base-0.94.0.0
14+- regex-tdfa-1.3.1.0
15+- haddock-library-1.8.0
16+- ghc-check-0.3.0.1
17+nix:
18+ packages: [zlib]
19+flags:
20+ ghcide:
21+ ghc-lib: True
22+ghc-options:
23+ ghc-lib-parser: -O0
24+ ghc-lib: -O0
stack.yaml+14−9
@@ -1,14 +1,19 @@
1−resolver: nightly-2019-05-20
1+resolver: nightly-2019-09-21
2packages:
2packages:
3- .
3- .
4−
5extra-deps:
4extra-deps:
6−- git: https://github.com/alanz/haskell-lsp.git
5+- haskell-lsp-0.22.0.0
7− commit: d73e2ccb518724e6766833ee3d7e73289cbe0018
8− subdirs:
9− - .
10− - haskell-lsp-types
6+- haskell-lsp-types-0.22.0.0
11−- git: https://github.com/mpickering/hie-bios.git
7+- lsp-test-0.11.0.1
12− commit: 8427e424a83c2f3d60bdd26c02478c00d2189a73
8+- hie-bios-0.5.0
9+- fuzzy-0.1.0.0
10+- regex-pcre-builtin-0.95.1.1.8.43
11+- regex-base-0.94.0.0
12+- regex-tdfa-1.3.1.0
13+- shake-0.18.5
14+- parser-combinators-1.2.1
15+- haddock-library-1.8.0
16+- tasty-rerun-1.1.17
17+- ghc-check-0.3.0.1
13nix:
18nix:
14 packages: [zlib]
19 packages: [zlib]
stack810.yamlnew file+18
@@ -0,0 +1,18 @@
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.yamlnew file+33
@@ -0,0 +1,33 @@
1+resolver: lts-12.26
2+packages:
3+- .
4+
5+extra-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
11+- rope-utf16-splay-0.3.1.0
12+- filepattern-0.1.1
13+- js-dgtable-0.5.2
14+- hie-bios-0.5.0
15+- fuzzy-0.1.0.0
16+- shake-0.18.5
17+- time-compat-1.9.2.2
18+- regex-base-0.94.0.0
19+- regex-tdfa-1.3.1.0
20+- 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+
32+nix:
33+ packages: [zlib]
stack88.yamlnew file+11
@@ -0,0 +1,11 @@
1+resolver: nightly-2020-02-13
2+packages:
3+- .
4+extra-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
10+nix:
11+ packages: [zlib]
test/cabal/Development/IDE/Test/Runfiles.hsnew file+9
@@ -0,0 +1,9 @@
1+-- Copyright (c) 2019 The DAML Authors. All rights reserved.
2+-- SPDX-License-Identifier: Apache-2.0
3+
4+module Development.IDE.Test.Runfiles
5+ ( locateGhcideExecutable
6+ ) where
7+
8+locateGhcideExecutable :: IO FilePath
9+locateGhcideExecutable = pure "ghcide"
test/data/hover/Bar.hsnew file+3
@@ -0,0 +1,3 @@
1+module Bar (Bar(..)) where
2+
3+data Bar = Bar
test/data/hover/Foo.hsnew file+5
@@ -0,0 +1,5 @@
1+module Foo (Bar, foo) where
2+
3+import Bar
4+
5+foo = Bar
test/data/hover/GotoHover.hsnew file+57
@@ -0,0 +1,57 @@
1+{-# LANGUAGE OverloadedStrings #-}
2+{- HLINT ignore -}
3+module Testing ( module Testing ) where
4+import Data.Text (Text, pack)
5+import Foo (Bar, foo)
6+
7+
8+data TypeConstructor = DataConstructor
9+ { fff :: Text
10+ , ggg :: Int }
11+aaa :: TypeConstructor
12+aaa = DataConstructor
13+ { fff = "dfgy"
14+ , ggg = 832
15+ }
16+bbb :: TypeConstructor
17+bbb = DataConstructor "mjgp" 2994
18+ccc :: (Text, Int)
19+ccc = (fff bbb, ggg aaa)
20+ddd :: Num a => a -> a -> a
21+ddd vv ww = vv +! ww
22+a +! b = a - b
23+hhh (Just a) (><) = a >< a
24+iii a b = a `b` a
25+jjj s = pack $ s <> s
26+class MyClass a where
27+ method :: a -> Int
28+instance MyClass Int where
29+ method = succ
30+kkk :: MyClass a => Int -> a -> Int
31+kkk n c = n + method c
32+
33+doBind :: Maybe ()
34+doBind = do unwrapped <- Just ()
35+ return unwrapped
36+
37+listCompBind :: [Char]
38+listCompBind = [ succ c | c <- "ptfx" ]
39+
40+multipleClause :: Bool -> Char
41+multipleClause True = 't'
42+multipleClause False = 'f'
43+
44+-- | Recognizable docs: kpqz
45+documented :: Monad m => Either Int (m a)
46+documented = Left 7518
47+
48+listOfInt = [ 8391 :: Int, 6268 ]
49+
50+outer :: Bool
51+outer = undefined where
52+
53+ inner :: Char
54+ inner = undefined
55+
56+imported :: Bar
57+imported = foo
test/data/multi/a/A.hsnew file+3
@@ -0,0 +1,3 @@
1+module A(foo) where
2+
3+foo = ()
test/data/multi/a/a.cabalnew file+9
@@ -0,0 +1,9 @@
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.hsnew file+3
@@ -0,0 +1,3 @@
1+module B(module B) where
2+import A
3+qux = foo
test/data/multi/b/b.cabalnew file+9
@@ -0,0 +1,9 @@
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.projectnew file+1
@@ -0,0 +1 @@
1+packages: a b
test/data/multi/hie.yamlnew file+6
@@ -0,0 +1,6 @@
1+cradle:
2+ cabal:
3+ - path: "./a"
4+ component: "lib:a"
5+ - path: "./b"
6+ component: "lib:b"
test/exe/Main.hsnew file+2496

This diff is too big to show here.

test/manual/lhs/Bird.lhsnew file+19

This diff is too big to show here.

test/manual/lhs/Main.hsnew file+12

This diff is too big to show here.

test/manual/lhs/Test.lhsnew file+36

This diff is too big to show here.

test/preprocessor/Main.hsnew file+10

This diff is too big to show here.

test/src/Development/IDE/Test.hsnew file+111

This diff is too big to show here.