on master help sign in
115 files changed+18071−3193
since splitsnapshots
unifiedsplit
.azure/linux-stack.yml+63 .azure/windows-stack.yml+68 .editorconfig+11 .ghci−16 .gitignore+10 .hlint.yaml+132 BUILD.bazel−85 CHANGELOG.md+85 LICENSE+201 README.md+334 azure-pipelines.yml+18 bench/Main.hs+287 cbits/getmodtime.c+21 docs/Setup.md+145 exe/Arguments.hs+35 exe/Main.hs+683 exe/Utils.hs+9 extension/.gitignore+4 extension/.vscodeignore+10 extension/README.md+1 extension/package-lock.json+750 extension/package.json+72 extension/src/extension.ts+45 extension/tsconfig.json+21 extension/tslint.json+15 fmt.sh+3 ghcide.cabal+344 haskell-ide-core.cabal−118 hie.yaml+12 img/vscode2.png include/ghc-api-version.h+10 install.bat+6 src-ghc810/Development/IDE/GHC/HieAst.hs+1928 src-ghc88/Development/IDE/GHC/HieAst.hs+1789 src/Development/IDE/Compat.hs+11−30 src/Development/IDE/Core/Compile.hs+598 src/Development/IDE/Core/Debouncer.hs+52 src/Development/IDE/Core/FileExists.hs+181 src/Development/IDE/Core/FileStore.hs+187 src/Development/IDE/Core/IdeConfiguration.hs+71 src/Development/IDE/Core/OfInterest.hs+82 src/Development/IDE/Core/PositionMapping.hs+122 src/Development/IDE/Core/Preprocessor.hs+230 src/Development/IDE/State/RuleTypes.hs → src/Development/IDE/Core/RuleTypes.hs+92−80 src/Development/IDE/Core/Rules.hs+671 src/Development/IDE/Core/Service.hs+107 src/Development/IDE/Core/Shake.hs+849 src/Development/IDE/Functions/AtPoint.hs−139 src/Development/IDE/Functions/Compile.hs−455 src/Development/IDE/Functions/DependencyInformation.hs−210 src/Development/IDE/Functions/FindImports.hs−148 src/Development/IDE/Functions/GHCError.hs−165 src/Development/IDE/Functions/SpanInfo.hs−134 src/Development/IDE/Functions/CPP.hs → src/Development/IDE/GHC/CPP.hs+52−10 src/Development/IDE/GHC/Compat.hs+309 src/Development/IDE/GHC/Error.hs+202 src/Development/IDE/GHC/Orphans.hs+72 src/Development/IDE/GHC/Util.hs+315 src/Development/IDE/Functions/Warnings.hs → src/Development/IDE/GHC/Warnings.hs+13−13 src/Development/IDE/GHC/WithDynFlags.hs+30 src/Development/IDE/Import/DependencyInformation.hs+376 src/Development/IDE/Import/FindImports.hs+179 src/Development/IDE/LSP/HoverDefinition.hs+62 src/Development/IDE/LSP/LanguageServer.hs+236 src/Development/IDE/LSP/Notifications.hs+81 src/Development/IDE/LSP/Outline.hs+234 src/Development/IDE/LSP/Protocol.hs+23 src/Development/IDE/LSP/Server.hs+47 src/Development/IDE/Logger.hs−26 src/Development/IDE/Orphans.hs−40 src/Development/IDE/Plugin.hs+60 src/Development/IDE/Plugin/CodeAction.hs+570 src/Development/IDE/Plugin/CodeAction/PositionIndexed.hs+112 src/Development/IDE/Plugin/CodeAction/RuleTypes.hs+61 src/Development/IDE/Plugin/CodeAction/Rules.hs+61 src/Development/IDE/Plugin/Completions.hs+103 src/Development/IDE/Plugin/Completions/Logic.hs+530 src/Development/IDE/Plugin/Completions/Types.hs+58 src/Development/IDE/Spans/AtPoint.hs+178 src/Development/IDE/Spans/Calculate.hs+273 src/Development/IDE/Spans/Common.hs+162 src/Development/IDE/Functions/Documentation.hs → src/Development/IDE/Spans/Documentation.hs+51−23 src/Development/IDE/Types/SpanInfo.hs → src/Development/IDE/Spans/Type.hs+34−20 src/Development/IDE/State/FileStore.hs−153 src/Development/IDE/State/Rules.hs−336 src/Development/IDE/State/Service.hs−120 src/Development/IDE/State/Shake.hs−426 src/Development/IDE/Types/Diagnostics.hs+84−128 src/Development/IDE/Types/LSP.hs−61 src/Development/IDE/Types/Location.hs+93−59 src/Development/IDE/Types/Logger.hs+54 src/Development/IDE/Types/Options.hs+99−7 src/Development/IDE/UtilGHC.hs−86 stack-ghc-lib.yaml+24 stack.yaml+17−1 stack810.yaml+18 stack84.yaml+33 stack88.yaml+11 test/Demo.hs−104 test/cabal/Development/IDE/Test/Runfiles.hs+9 test/data/hover/Bar.hs+3 test/data/hover/Foo.hs+5 test/data/hover/GotoHover.hs+57 test/data/multi/a/A.hs+3 test/data/multi/a/a.cabal+9 test/data/multi/b/B.hs+3 test/data/multi/b/b.cabal+9 test/data/multi/cabal.project+1 test/data/multi/hie.yaml+6 test/exe/Main.hs+2496 test/manual/lhs/Bird.lhs+19 test/manual/lhs/Main.hs+12 test/manual/lhs/Test.lhs+36 test/preprocessor/Main.hs+10 test/src/Development/IDE/Test.hs+111
.azure/linux-stack.ymlnew file+63
@@ -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
.ghcideleted−16
@@ -1,16 +0,0 @@
1−:set -ignore-package=ghc-lib -ignore-package=ghc-parser -package=ghc
2−:set -fwarn-unused-binds -fwarn-unused-imports -fwarn-orphans
3−:set -isrc -i../../libs-haskell/prettyprinter-syntax/src
4−:set -DGHC_STABLE
5−:set -XLambdaCase
6−:set -XBangPatterns
7−:set -XDeriveGeneric
8−:set -XRecordWildCards
9−:set -XScopedTypeVariables
10−:set -XNamedFieldPuns
11−:set -XTupleSections
12−:set -XTypeApplications
13−:set -XViewPatterns
14−:set -XGeneralizedNewtypeDeriving
15−:set -XStandaloneDeriving
16−:load test/Demo.hs
.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−85
@@ -1,85 +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− "base",
10− "binary",
11− "bytestring",
12− "containers",
13− "deepseq",
14− "directory",
15− "either",
16− "extra",
17− "filepath",
18− "hashable",
19− "haskell-lsp",
20− "haskell-lsp-types",
21− "mtl",
22− "pretty",
23− "safe-exceptions",
24− "shake",
25− "stm",
26− "syb",
27− "text",
28− "time",
29− "transformers",
30− "uniplate",
31− "unordered-containers",
32− "uri-encode",
33−]
34−
35−da_haskell_library(
36− name = "haskell-ide-core",
37− srcs = glob(["src/**/*.hs"]),
38− hazel_deps = depends + [
39− "ghc-lib",
40− "ghc-lib-parser",
41− ],
42− src_strip_prefix = "src",
43− visibility = ["//visibility:public"],
44− deps = [
45− "//libs-haskell/prettyprinter-syntax",
46− ],
47−)
48−
49−da_haskell_library(
50− name = "haskell-ide-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− src_strip_prefix = "src",
59− visibility = ["//visibility:public"],
60− deps = [
61− "//libs-haskell/prettyprinter-syntax",
62− ],
63−)
64−
65−da_haskell_binary(
66− name = "haskell-ide-core-demo",
67− srcs = glob(["test/**/*.hs"]),
68− hazel_deps = [
69− "base",
70− "containers",
71− "extra",
72− "filepath",
73− "ghc-paths",
74− "ghc",
75− "shake",
76− "text",
77− ],
78− main_function = "Demo.main",
79− src_strip_prefix = "test",
80− visibility = ["//visibility:public"],
81− deps = [
82− "haskell-ide-core-public",
83− "//libs-haskell/prettyprinter-syntax",
84− ],
85−) 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.mdnew file+334
@@ -0,0 +1,334 @@
1+# `ghcide` - A library for building Haskell IDE tooling
2+
3+Note: `ghcide` was previously called `hie-core`.
4+
5+Our vision is that you should build an IDE by combining:
6+
7+![vscode](https://raw.githubusercontent.com/digital-asset/ghcide/master/img/vscode2.png)
8+
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;
10+* `ghcide` (i.e. this library) for defining how to type check, when to type check, and producing diagnostic messages;
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;
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;
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).
14+
15+There are more details about our approach [in this blog post](https://4ta.uk/p/shaking-up-the-ide).
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+
45+## Using it
46+
47+### Install `ghcide`
48+
49+#### With Nix
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+
67+1. `git clone https://github.com/digital-asset/ghcide.git`
68+2. `cd ghcide`
69+3. `cabal install` or `stack install` (and make sure `~/.local/bin` is on your `$PATH`)
70+
71+It's important that `ghcide` is compiled with the same compiler you use to build your projects.
72+
73+### Test `ghcide`
74+
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:
76+
77+```console
78+Files that failed:
79+ * .\model\Main.hs
80+ * .\model\Model.hs
81+ * .\model\Test.hs
82+ * .\model\Util.hs
83+ * .\output\docs\Main.hs
84+ * .\output\docs\Part_Architecture_md.hs
85+Completed (152 worked, 6 failed)
86+```
87+
88+Of 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.
89+
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
95+
96+`ghcide` has been designed to handle projects with hundreds or thousands of modules. If `ghci` can handle it, then `ghcide` should be able to handle it. The only caveat is that this currently requires GHC >= 8.6, and that the first time a module is loaded in the editor will trigger generation of support files in the background if those do not already exist.
97+
98+### Using with VS Code
99+
100+You can install the VSCode extension from the [VSCode
101+marketplace](https://marketplace.visualstudio.com/items?itemName=DigitalAssetHoldingsLLC.ghcide).
102+
103+### Using with Atom
104+
105+You can follow the [instructions](https://github.com/moodmosaic/ide-haskell-ghcide#readme) to install with `apm`.
106+
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+```
126+
127+### Using with Emacs
128+
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
139+
140+```elisp
141+;; LSP
142+(use-package flycheck
143+ :ensure t
144+ :init
145+ (global-flycheck-mode t))
146+(use-package yasnippet
147+ :ensure t)
148+(use-package lsp-mode
149+ :ensure t
150+ :hook (haskell-mode . lsp)
151+ :commands lsp)
152+(use-package lsp-ui
153+ :ensure t
154+ :commands lsp-ui-mode)
155+(use-package lsp-haskell
156+ :ensure t
157+ :config
158+ (setq lsp-haskell-process-path-hie "ghcide")
159+ (setq lsp-haskell-process-args-hie '())
160+ ;; Comment/uncomment this line to see interactions between lsp client/server.
161+ ;;(setq lsp-log-io t)
162+)
163+```
164+
165+#### eglot
166+
167+````elisp
168+(use-package eglot
169+ :ensure t
170+ :config
171+ (add-to-list 'eglot-server-programs '(haskell-mode . ("ghcide" "--lsp"))))
172+````
173+
174+### Using with Vim/Neovim
175+
176+#### LanguageClient-neovim
177+Install [LanguageClient-neovim](https://github.com/autozimu/LanguageClient-neovim)
178+
179+Add this to your vim config:
180+```vim
181+let g:LanguageClient_rootMarkers = ['*.cabal', 'stack.yaml']
182+let g:LanguageClient_serverCommands = {
183+ \ 'rust': ['rls'],
184+ \ 'haskell': ['ghcide', '--lsp'],
185+ \ }
186+```
187+
188+Refer to `:he LanguageClient` for more details on usage and configuration.
189+
190+#### vim-lsp
191+Install [vim-lsp](https://github.com/prabirshrestha/vim-lsp).
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`.
204+
205+### coc.nvim
206+
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"
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.hsnew file+35
@@ -0,0 +1,35 @@
1+-- Copyright (c) 2019 The DAML Authors. All rights reserved.
2+-- SPDX-License-Identifier: Apache-2.0
3+
4+module Arguments(Arguments(..), getArguments) where
5+
6+import Options.Applicative
7+
8+
9+data Arguments = Arguments
10+ {argLSP :: Bool
11+ ,argsCwd :: Maybe FilePath
12+ ,argFiles :: [FilePath]
13+ ,argsVersion :: Bool
14+ ,argsShakeProfiling :: Maybe FilePath
15+ ,argsTesting :: Bool
16+ ,argsThreads :: Int
17+ }
18+
19+getArguments :: IO Arguments
20+getArguments = execParser opts
21+ where
22+ opts = info (arguments <**> helper)
23+ ( fullDesc
24+ <> progDesc "Used as a test bed to check your IDE will work"
25+ <> header "ghcide - the core of a Haskell IDE")
26+
27+arguments :: Parser Arguments
28+arguments = Arguments
29+ <$> switch (long "lsp" <> help "Start talking to an LSP server")
30+ <*> optional (strOption $ long "cwd" <> metavar "DIR" <> help "Change to this directory")
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.hsnew file+683
@@ -0,0 +1,683 @@
1+-- Copyright (c) 2019 The DAML Authors. All rights reserved.
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 #-}
7+
8+module Main(main) where
9+
10+import Linker (initDynLinker)
11+import Data.IORef
12+import NameCache
13+import Packages
14+import Module
15+import Arguments
16+import Control.Concurrent.Async
17+import Control.Concurrent.Extra
18+import Control.Exception
19+import Control.Monad.Extra
20+import Control.Monad.IO.Class
21+import Data.Default
22+import Data.Either
23+import Data.Function
24+import Data.List.Extra
25+import Data.Maybe
26+import qualified Data.Text as T
27+import qualified Data.Text.IO as T
28+import Data.Time.Clock (UTCTime)
29+import Data.Version
30+import Development.IDE.Core.Debouncer
31+import Development.IDE.Core.FileStore
32+import Development.IDE.Core.OfInterest
33+import Development.IDE.Core.Service
34+import Development.IDE.Core.Rules
35+import Development.IDE.Core.Shake
36+import Development.IDE.Core.RuleTypes
37+import Development.IDE.LSP.Protocol
38+import Development.IDE.Types.Location
39+import Development.IDE.Types.Diagnostics
40+import Development.IDE.Types.Options
41+import Development.IDE.Types.Logger
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
47+import Language.Haskell.LSP.Messages
48+import Language.Haskell.LSP.Types
49+import Data.Aeson (ToJSON(toJSON))
50+import Development.IDE.LSP.LanguageServer
51+import qualified System.Directory.Extra as IO
52+import System.Environment
53+import 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
61+import Development.Shake (Action, action)
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
74+
75+import HIE.Bios.Cradle
76+import HIE.Bios.Types
77+
78+import Utils
79+
80+ghcideVersion :: IO String
81+ghcideVersion = do
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
90+
91+main :: IO ()
92+main = do
93+ -- WARNING: If you write to stdout before runLanguageServer
94+ -- then the language server will not work
95+ Arguments{..} <- getArguments
96+
97+ if argsVersion then ghcideVersion >>= putStrLn >> exitSuccess
98+ else hPutStrLn stderr {- see WARNING above -} =<< ghcideVersion
99+
100+ -- lock to avoid overlapping output on stdout
101+ lock <- newLock
102+ let logger p = Logger $ \pri msg -> when (pri >= p) $ withLock lock $
103+ T.putStrLn $ T.pack ("[" ++ upper (show pri) ++ "] ") <> msg
104+
105+ whenJust argsCwd IO.setCurrentDirectory
106+
107+ dir <- IO.getCurrentDirectory
108+ command <- makeLspCommandId "typesignature.add"
109+
110+ let plugins = Completions.plugin <> CodeAction.plugin
111+ onInitialConfiguration = const $ Right ()
112+ onConfigurationChange = const $ Right ()
113+ options = def { LSP.executeCommandCommands = Just [command]
114+ , LSP.completionTriggerCharacters = Just "."
115+ }
116+
117+ if argLSP then do
118+ t <- offsetTime
119+ hPutStrLn stderr "Starting LSP server..."
120+ hPutStrLn stderr "If you are seeing this in a terminal, you probably should have run ghcide WITHOUT the --lsp option!"
121+ runLanguageServer options (pluginHandler plugins) onInitialConfiguration onConfigurationChange $ \getLspId event vfs caps -> do
122+ t <- t
123+ hPutStrLn stderr $ "Started LSP server in " ++ showDuration t
124+ let options = (defaultIdeOptions $ loadSessionShake dir)
125+ { optReportProgress = clientSupportsProgress caps
126+ , optShakeProfiling = argsShakeProfiling
127+ , optTesting = argsTesting
128+ , optThreads = argsThreads
129+ }
130+ debouncer <- newAsyncDebouncer
131+ initialise caps (mainRule >> pluginRules plugins >> action kick)
132+ getLspId event (logger minBound) debouncer options vfs
133+ else do
134+ -- GHC produces messages with UTF8 in them, so make sure the terminal doesn't error
135+ hSetEncoding stdout utf8
136+ hSetEncoding stderr utf8
137+
138+ putStrLn $ "Ghcide setup tester in " ++ dir ++ "."
139+ putStrLn "Report bugs at https://github.com/digital-asset/ghcide/issues"
140+
141+ putStrLn $ "\nStep 1/4: Finding files to test in " ++ dir
142+ files <- expandFiles (argFiles ++ ["." | null argFiles])
143+ -- LSP works with absolute file paths, so try and behave similarly
144+ files <- nubOrd <$> mapM IO.canonicalizePath files
145+ putStrLn $ "Found " ++ show (length files) ++ " files"
146+
147+ putStrLn "\nStep 2/4: Looking for hie.yaml files that control setup"
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
156+
157+ putStrLn "\nStep 4/4: Type checking the files"
158+ setFilesOfInterest ide $ HashSet.fromList $ map toNormalizedFilePath' files
159+ results <- runActionSync ide $ uses TypeCheck (map toNormalizedFilePath' files)
160+ let (worked, failed) = partition fst $ zip (map isJust results) files
161+ when (failed /= []) $
162+ putStr $ unlines $ "Files that failed:" : map ((++) " * " . snd) failed
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 ()
167+
168+expandFiles :: [FilePath] -> IO [FilePath]
169+expandFiles = concatMapM $ \x -> do
170+ b <- IO.doesFileExist x
171+ if b then return [x] else do
172+ let recurse "." = True
173+ recurse x | "." `isPrefixOf` takeFileName x = False -- skip .git etc
174+ recurse x = takeFileName x `notElem` ["dist","dist-newstyle"] -- cabal directories
175+ files <- filter (\x -> takeExtension x `elem` [".hs",".lhs"]) <$> IO.listFilesInside (return . recurse) x
176+ when (null files) $
177+ fail $ "Couldn't find any .hs/.lhs files inside directory: " ++ x
178+ return files
179+
180+
181+kick :: Action ()
182+kick = do
183+ files <- getFilesOfInterest
184+ void $ uses TypeCheck $ HashSet.toList files
185+
186+-- | Print an LSP event.
187+showEvent :: Lock -> FromServerMessage -> IO ()
188+showEvent _ (EventFileDiagnostics _ []) = return ()
189+showEvent lock (EventFileDiagnostics (toNormalizedFilePath' -> file) diags) =
190+ withLock lock $ T.putStrLn $ showDiagnosticsColored $ map (file,ShowDiag,) diags
191+showEvent lock e = withLock lock $ print e
192+
193+
194+-- | Run the specific cradle on a specific FilePath via hie-bios.
195+cradleToSessionOpts :: Cradle a -> FilePath -> IO (Either [CradleError] ComponentOptions)
196+cradleToSessionOpts cradle file = do
197+ let showLine s = putStrLn ("> " ++ s)
198+ cradleRes <- runCradle (cradleOptsProg cradle) showLine file
199+ case cradleRes of
200+ CradleSuccess r -> pure (Right r)
201+ CradleFail err -> return (Left [err])
202+ -- For the None cradle perhaps we still want to report an Info
203+ -- message about the fact that the file is being ignored.
204+ CradleNone -> return (Left [])
205+
206+emptyHscEnv :: IO HscEnv
207+emptyHscEnv = do
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/.gitignorenew file+4
@@ -0,0 +1,4 @@
1+out
2+node_modules
3+.vscode-test/
4+*.vsix
extension/.vscodeignorenew file+10
@@ -0,0 +1,10 @@
1+.vscode/**
2+.vscode-test/**
3+out/test/**
4+src/**
5+.gitignore
6+vsc-extension-quickstart.md
7+**/tsconfig.json
8+**/tslint.json
9+**/*.map
10+**/*.ts
extension/README.mdnew file+1
@@ -0,0 +1 @@
1+../README.md
extension/package-lock.jsonnew file+750
@@ -0,0 +1,750 @@
1+{
2+ "name": "ghcide",
3+ "version": "0.0.2",
4+ "lockfileVersion": 1,
5+ "requires": true,
6+ "dependencies": {
7+ "@babel/code-frame": {
8+ "version": "7.5.5",
9+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz",
10+ "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==",
11+ "dev": true,
12+ "requires": {
13+ "@babel/highlight": "^7.0.0"
14+ }
15+ },
16+ "@babel/highlight": {
17+ "version": "7.5.0",
18+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.5.0.tgz",
19+ "integrity": "sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==",
20+ "dev": true,
21+ "requires": {
22+ "chalk": "^2.0.0",
23+ "esutils": "^2.0.2",
24+ "js-tokens": "^4.0.0"
25+ }
26+ },
27+ "@types/mocha": {
28+ "version": "5.2.7",
29+ "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.7.tgz",
30+ "integrity": "sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==",
31+ "dev": true
32+ },
33+ "@types/node": {
34+ "version": "12.7.11",
35+ "resolved": "https://registry.npmjs.org/@types/node/-/node-12.7.11.tgz",
36+ "integrity": "sha512-Otxmr2rrZLKRYIybtdG/sgeO+tHY20GxeDjcGmUnmmlCWyEnv2a2x1ZXBo3BTec4OiTXMQCiazB8NMBf0iRlFw==",
37+ "dev": true
38+ },
39+ "@types/vscode": {
40+ "version": "1.35.0",
41+ "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.35.0.tgz",
42+ "integrity": "sha512-Iyliuu8Hv4qy4TEaevQzChh9UsTEcuaKdcHXBbvJnoJSF5Td2yNENOrPK+vuOaXJJBhQZb4BNJKOxt6caaQR8A==",
43+ "dev": true
44+ },
45+ "ansi-styles": {
46+ "version": "3.2.1",
47+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
48+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
49+ "dev": true,
50+ "requires": {
51+ "color-convert": "^1.9.0"
52+ }
53+ },
54+ "argparse": {
55+ "version": "1.0.10",
56+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
57+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
58+ "dev": true,
59+ "requires": {
60+ "sprintf-js": "~1.0.2"
61+ }
62+ },
63+ "azure-devops-node-api": {
64+ "version": "7.2.0",
65+ "resolved": "https://registry.npmjs.org/azure-devops-node-api/-/azure-devops-node-api-7.2.0.tgz",
66+ "integrity": "sha512-pMfGJ6gAQ7LRKTHgiRF+8iaUUeGAI0c8puLaqHLc7B8AR7W6GJLozK9RFeUHFjEGybC9/EB3r67WPd7e46zQ8w==",
67+ "dev": true,
68+ "requires": {
69+ "os": "0.1.1",
70+ "tunnel": "0.0.4",
71+ "typed-rest-client": "1.2.0",
72+ "underscore": "1.8.3"
73+ }
74+ },
75+ "balanced-match": {
76+ "version": "1.0.0",
77+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
78+ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
79+ "dev": true
80+ },
81+ "boolbase": {
82+ "version": "1.0.0",
83+ "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
84+ "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=",
85+ "dev": true
86+ },
87+ "brace-expansion": {
88+ "version": "1.1.11",
89+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
90+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
91+ "dev": true,
92+ "requires": {
93+ "balanced-match": "^1.0.0",
94+ "concat-map": "0.0.1"
95+ }
96+ },
97+ "buffer-crc32": {
98+ "version": "0.2.13",
99+ "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
100+ "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=",
101+ "dev": true
102+ },
103+ "builtin-modules": {
104+ "version": "1.1.1",
105+ "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
106+ "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=",
107+ "dev": true
108+ },
109+ "chalk": {
110+ "version": "2.4.2",
111+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
112+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
113+ "dev": true,
114+ "requires": {
115+ "ansi-styles": "^3.2.1",
116+ "escape-string-regexp": "^1.0.5",
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"
132+ }
133+ },
134+ "color-convert": {
135+ "version": "1.9.3",
136+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
137+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
138+ "dev": true,
139+ "requires": {
140+ "color-name": "1.1.3"
141+ }
142+ },
143+ "color-name": {
144+ "version": "1.1.3",
145+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
146+ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
147+ "dev": true
148+ },
149+ "commander": {
150+ "version": "2.20.1",
151+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.1.tgz",
152+ "integrity": "sha512-cCuLsMhJeWQ/ZpsFTbE765kvVfoeSddc4nU3up4fV+fDBcfUXnbITJ+JzhkdjzOqhURjZgujxaioam4RM9yGUg==",
153+ "dev": true
154+ },
155+ "concat-map": {
156+ "version": "0.0.1",
157+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
158+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
159+ "dev": true
160+ },
161+ "css-select": {
162+ "version": "1.2.0",
163+ "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz",
164+ "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=",
165+ "dev": true,
166+ "requires": {
167+ "boolbase": "~1.0.0",
168+ "css-what": "2.1",
169+ "domutils": "1.5.1",
170+ "nth-check": "~1.0.1"
171+ }
172+ },
173+ "css-what": {
174+ "version": "2.1.3",
175+ "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz",
176+ "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==",
177+ "dev": true
178+ },
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": {
186+ "version": "1.2.1",
187+ "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.1.tgz",
188+ "integrity": "sha1-6S7f2tplN9SE1zwBcv0eugxJdv8=",
189+ "dev": true
190+ },
191+ "diff": {
192+ "version": "4.0.1",
193+ "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.1.tgz",
194+ "integrity": "sha512-s2+XdvhPCOF01LRQBC8hf4vhbVmI2CGS5aZnxLJlT5FtdhPCDFq80q++zK2KlrVorVDdL5BOGZ/VfLrVtYNF+Q==",
195+ "dev": true
196+ },
197+ "dom-serializer": {
198+ "version": "0.1.1",
199+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz",
200+ "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==",
201+ "dev": true,
202+ "requires": {
203+ "domelementtype": "^1.3.0",
204+ "entities": "^1.1.1"
205+ }
206+ },
207+ "domelementtype": {
208+ "version": "1.3.1",
209+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz",
210+ "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==",
211+ "dev": true
212+ },
213+ "domhandler": {
214+ "version": "2.4.2",
215+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz",
216+ "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==",
217+ "dev": true,
218+ "requires": {
219+ "domelementtype": "1"
220+ }
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+ },
238+ "escape-string-regexp": {
239+ "version": "1.0.5",
240+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
241+ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
242+ "dev": true
243+ },
244+ "esprima": {
245+ "version": "4.0.1",
246+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
247+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
248+ "dev": true
249+ },
250+ "esutils": {
251+ "version": "2.0.3",
252+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
253+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
254+ "dev": true
255+ },
256+ "fd-slicer": {
257+ "version": "1.1.0",
258+ "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
259+ "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=",
260+ "dev": true,
261+ "requires": {
262+ "pend": "~1.2.0"
263+ }
264+ },
265+ "fs.realpath": {
266+ "version": "1.0.0",
267+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
268+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
269+ "dev": true
270+ },
271+ "glob": {
272+ "version": "7.1.4",
273+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz",
274+ "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==",
275+ "dev": true,
276+ "requires": {
277+ "fs.realpath": "^1.0.0",
278+ "inflight": "^1.0.4",
279+ "inherits": "2",
280+ "minimatch": "^3.0.4",
281+ "once": "^1.3.0",
282+ "path-is-absolute": "^1.0.0"
283+ }
284+ },
285+ "has-flag": {
286+ "version": "3.0.0",
287+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
288+ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
289+ "dev": true
290+ },
291+ "htmlparser2": {
292+ "version": "3.10.1",
293+ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz",
294+ "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==",
295+ "dev": true,
296+ "requires": {
297+ "domelementtype": "^1.3.1",
298+ "domhandler": "^2.3.0",
299+ "domutils": "^1.5.1",
300+ "entities": "^1.1.1",
301+ "inherits": "^2.0.1",
302+ "readable-stream": "^3.1.1"
303+ }
304+ },
305+ "inflight": {
306+ "version": "1.0.6",
307+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
308+ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
309+ "dev": true,
310+ "requires": {
311+ "once": "^1.3.0",
312+ "wrappy": "1"
313+ }
314+ },
315+ "inherits": {
316+ "version": "2.0.4",
317+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
318+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
319+ "dev": true
320+ },
321+ "js-tokens": {
322+ "version": "4.0.0",
323+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
324+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
325+ "dev": true
326+ },
327+ "js-yaml": {
328+ "version": "3.13.1",
329+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
330+ "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
331+ "dev": true,
332+ "requires": {
333+ "argparse": "^1.0.7",
334+ "esprima": "^4.0.0"
335+ }
336+ },
337+ "linkify-it": {
338+ "version": "2.2.0",
339+ "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.2.0.tgz",
340+ "integrity": "sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw==",
341+ "dev": true,
342+ "requires": {
343+ "uc.micro": "^1.0.1"
344+ }
345+ },
346+ "lodash": {
347+ "version": "4.17.15",
348+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
349+ "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
350+ "dev": true
351+ },
352+ "markdown-it": {
353+ "version": "8.4.2",
354+ "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-8.4.2.tgz",
355+ "integrity": "sha512-GcRz3AWTqSUphY3vsUqQSFMbgR38a4Lh3GWlHRh/7MRwz8mcu9n2IO7HOh+bXHrR9kOPDl5RNCaEsrneb+xhHQ==",
356+ "dev": true,
357+ "requires": {
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"
363+ }
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+ },
377+ "minimatch": {
378+ "version": "3.0.4",
379+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
380+ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
381+ "dev": true,
382+ "requires": {
383+ "brace-expansion": "^1.1.7"
384+ }
385+ },
386+ "minimist": {
387+ "version": "0.0.8",
388+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
389+ "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
390+ "dev": true
391+ },
392+ "mkdirp": {
393+ "version": "0.5.1",
394+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
395+ "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
396+ "dev": true,
397+ "requires": {
398+ "minimist": "0.0.8"
399+ }
400+ },
401+ "mute-stream": {
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",
409+ "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz",
410+ "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==",
411+ "dev": true,
412+ "requires": {
413+ "boolbase": "~1.0.0"
414+ }
415+ },
416+ "once": {
417+ "version": "1.4.0",
418+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
419+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
420+ "dev": true,
421+ "requires": {
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": "*"
477+ }
478+ },
479+ "path-is-absolute": {
480+ "version": "1.0.1",
481+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
482+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
483+ "dev": true
484+ },
485+ "path-parse": {
486+ "version": "1.0.6",
487+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
488+ "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
489+ "dev": true
490+ },
491+ "pend": {
492+ "version": "1.2.0",
493+ "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
494+ "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=",
495+ "dev": true
496+ },
497+ "read": {
498+ "version": "1.0.7",
499+ "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz",
500+ "integrity": "sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ=",
501+ "dev": true,
502+ "requires": {
503+ "mute-stream": "~0.0.4"
504+ }
505+ },
506+ "readable-stream": {
507+ "version": "3.4.0",
508+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz",
509+ "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==",
510+ "dev": true,
511+ "requires": {
512+ "inherits": "^2.0.3",
513+ "string_decoder": "^1.1.1",
514+ "util-deprecate": "^1.0.1"
515+ }
516+ },
517+ "resolve": {
518+ "version": "1.12.0",
519+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz",
520+ "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==",
521+ "dev": true,
522+ "requires": {
523+ "path-parse": "^1.0.6"
524+ }
525+ },
526+ "safe-buffer": {
527+ "version": "5.2.0",
528+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz",
529+ "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==",
530+ "dev": true
531+ },
532+ "semver": {
533+ "version": "6.3.0",
534+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
535+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
536+ },
537+ "sprintf-js": {
538+ "version": "1.0.3",
539+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
540+ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
541+ "dev": true
542+ },
543+ "string_decoder": {
544+ "version": "1.3.0",
545+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
546+ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
547+ "dev": true,
548+ "requires": {
549+ "safe-buffer": "~5.2.0"
550+ }
551+ },
552+ "supports-color": {
553+ "version": "5.5.0",
554+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
555+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
556+ "dev": true,
557+ "requires": {
558+ "has-flag": "^3.0.0"
559+ }
560+ },
561+ "tmp": {
562+ "version": "0.0.29",
563+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.29.tgz",
564+ "integrity": "sha1-8lEl/w3Z2jzLDC3Tce4SiLuRKMA=",
565+ "dev": true,
566+ "requires": {
567+ "os-tmpdir": "~1.0.1"
568+ }
569+ },
570+ "tslib": {
571+ "version": "1.10.0",
572+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",
573+ "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==",
574+ "dev": true
575+ },
576+ "tslint": {
577+ "version": "5.20.0",
578+ "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.20.0.tgz",
579+ "integrity": "sha512-2vqIvkMHbnx8acMogAERQ/IuINOq6DFqgF8/VDvhEkBqQh/x6SP0Y+OHnKth9/ZcHQSroOZwUQSN18v8KKF0/g==",
580+ "dev": true,
581+ "requires": {
582+ "@babel/code-frame": "^7.0.0",
583+ "builtin-modules": "^1.1.1",
584+ "chalk": "^2.3.0",
585+ "commander": "^2.12.1",
586+ "diff": "^4.0.1",
587+ "glob": "^7.1.1",
588+ "js-yaml": "^3.13.1",
589+ "minimatch": "^3.0.4",
590+ "mkdirp": "^0.5.1",
591+ "resolve": "^1.3.2",
592+ "semver": "^5.3.0",
593+ "tslib": "^1.8.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+ }
603+ }
604+ },
605+ "tsutils": {
606+ "version": "2.29.0",
607+ "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz",
608+ "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==",
609+ "dev": true,
610+ "requires": {
611+ "tslib": "^1.8.1"
612+ }
613+ },
614+ "tunnel": {
615+ "version": "0.0.4",
616+ "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.4.tgz",
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==",
624+ "dev": true,
625+ "requires": {
626+ "tunnel": "0.0.4",
627+ "underscore": "1.8.3"
628+ }
629+ },
630+ "typescript": {
631+ "version": "3.6.3",
632+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.3.tgz",
633+ "integrity": "sha512-N7bceJL1CtRQ2RiG0AQME13ksR7DiuQh/QehubYcghzv20tnh+MQnQIuJddTmsbqYj+dztchykemz0zFzlvdQw==",
634+ "dev": true
635+ },
636+ "uc.micro": {
637+ "version": "1.0.6",
638+ "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",
639+ "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==",
640+ "dev": true
641+ },
642+ "underscore": {
643+ "version": "1.8.3",
644+ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz",
645+ "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=",
646+ "dev": true
647+ },
648+ "url-join": {
649+ "version": "1.1.0",
650+ "resolved": "https://registry.npmjs.org/url-join/-/url-join-1.1.0.tgz",
651+ "integrity": "sha1-dBxsL0WWxIMNZxhGCSDQySIC3Hg=",
652+ "dev": true
653+ },
654+ "util-deprecate": {
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",
662+ "resolved": "https://registry.npmjs.org/vsce/-/vsce-1.68.0.tgz",
663+ "integrity": "sha512-yFbRYu4x4GbdQzZdEQQeRJBxgPdummgcUOFHUtnclW8XQl3MTuKgXL3TtI09gb5oq7jE6kdyvBmpBcmDGsmhcQ==",
664+ "dev": true,
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",
672+ "glob": "^7.0.6",
673+ "lodash": "^4.17.10",
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",
680+ "semver": "^5.1.0",
681+ "tmp": "0.0.29",
682+ "typed-rest-client": "1.2.0",
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+ }
694+ }
695+ },
696+ "vscode-jsonrpc": {
697+ "version": "5.0.0-next.2",
698+ "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-5.0.0-next.2.tgz",
699+ "integrity": "sha512-Q3/jabZUNviCG9hhF6hHWjhrABevPF9mv0aiE2j8BYCAP2k+aHTpjMyk+04MzaAqWYwXdQuZkLSbcYCCqbzJLg=="
700+ },
701+ "vscode-languageclient": {
702+ "version": "6.0.0-next.1",
703+ "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-6.0.0-next.1.tgz",
704+ "integrity": "sha512-eJ9VjLFNINArgRzLbQ11YlWry7dM93GEODkQBXTRfrSypksiO9qSGr4SHhWgxxP26p4FRSpzc/17+N+Egnnchg==",
705+ "requires": {
706+ "semver": "^6.3.0",
707+ "vscode-languageserver-protocol": "^3.15.0-next.9"
708+ }
709+ },
710+ "vscode-languageserver-protocol": {
711+ "version": "3.15.0-next.9",
712+ "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.0-next.9.tgz",
713+ "integrity": "sha512-b9PAxouMmtsLEe8ZjbIMPb7wRWPhckGfgjwZLmp/dWnaAuRPYtY3lGO0/rNbLc3jKIqCVlnEyYVFKalzDAzj0g==",
714+ "requires": {
715+ "vscode-jsonrpc": "^5.0.0-next.2",
716+ "vscode-languageserver-types": "^3.15.0-next.5"
717+ }
718+ },
719+ "vscode-languageserver-types": {
720+ "version": "3.15.0-next.5",
721+ "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.15.0-next.5.tgz",
722+ "integrity": "sha512-7hrELhTeWieUgex3+6692KjCkcmO/+V/bFItM5MHGcBotzwmjEuXjapLLYTYhIspuJ1ibRSik5MhX5YwLpsPiw=="
723+ },
724+ "wrappy": {
725+ "version": "1.0.2",
726+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
727+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
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+ }
748+ }
749+ }
750+}
extension/package.jsonnew file+72
@@ -0,0 +1,72 @@
1+{
2+ "name": "ghcide",
3+ "displayName": "ghcide",
4+ "publisher": "DigitalAssetHoldingsLLC",
5+ "repository": {
6+ "type": "git",
7+ "url": "https://github.com/digital-asset/ghcide.git"
8+ },
9+ "description": "A simple extension to test out haskell ide core",
10+ "version": "0.0.2",
11+ "license": "Apache-2.0",
12+ "engines": {
13+ "vscode": "^1.35.0"
14+ },
15+ "categories": [
16+ "Other"
17+ ],
18+ "activationEvents": [
19+ "onLanguage:haskell"
20+ ],
21+ "main": "./out/extension.js",
22+ "contributes": {
23+ "languages": [
24+ {
25+ "id": "haskell",
26+ "extensions": [
27+ "hs",
28+ "hs-boot",
29+ "lhs-boot",
30+ "lhs"
31+ ]
32+ }
33+ ],
34+ "configuration": {
35+ "type": "object",
36+ "title": "Haskell IDE Core Configuration",
37+ "properties": {
38+ "hic.executablePath": {
39+ "type": "string",
40+ "default": "ghcide",
41+ "description": "The location of your ghcide executable"
42+ },
43+ "hic.arguments": {
44+ "type": "string",
45+ "default": "--lsp",
46+ "description": "The arguments you would like to pass to the executable"
47+ }
48+ }
49+ }
50+ },
51+ "scripts": {
52+ "vscode:prepublish": "npm run compile",
53+ "compile": "tsc -p ./",
54+ "watch": "tsc -watch -p ./",
55+ "test": "npm run compile && node ./node_modules/vscode/bin/test",
56+ "vscepackage": "vsce package"
57+ },
58+ "extensionDependencies": [
59+ "justusadam.language-haskell"
60+ ],
61+ "dependencies": {
62+ "vscode-languageclient": "^6.0.0-next.1"
63+ },
64+ "devDependencies": {
65+ "@types/mocha": "^5.2.7",
66+ "@types/node": "^12.7.11",
67+ "@types/vscode": "1.35.0",
68+ "tslint": "^5.12.1",
69+ "typescript": "^3.6.3",
70+ "vsce": "^1.68.0"
71+ }
72+}
extension/src/extension.tsnew file+45
@@ -0,0 +1,45 @@
1+// Copyright (c) 2019 The DAML Authors. All rights reserved.
2+// SPDX-License-Identifier: Apache-2.0
3+
4+import * as path from 'path';
5+import { workspace, ExtensionContext, window } from 'vscode';
6+
7+import {
8+ LanguageClient,
9+ LanguageClientOptions,
10+ ServerOptions,
11+ TransportKind
12+} from 'vscode-languageclient';
13+
14+let client: LanguageClient;
15+
16+export function activate(context: ExtensionContext) {
17+ let config = workspace.getConfiguration("hic");
18+ let cPath: string = config.get("executablePath") as string;
19+ if(cPath === "" || cPath === undefined){
20+ window.showErrorMessage("You must specify a hic.executionPath in config");
21+ return;
22+ }
23+ let argString = config.get("arguments") as string;
24+
25+ let args : string[] = argString.split(" ");
26+
27+ let clientOptions: LanguageClientOptions = {
28+ // Register the server for plain text documents
29+ documentSelector: ["haskell"]
30+ };
31+ client = new LanguageClient(
32+ 'haskell',
33+ 'ghcide',
34+ { args: args, command: cPath, options: {cwd: workspace.rootPath }}, clientOptions, true);
35+ client.registerProposedFeatures();
36+
37+ client.start();
38+}
39+
40+export function deactivate(): Thenable<void> | undefined {
41+ if (!client) {
42+ return undefined;
43+ }
44+ return client.stop();
45+}
extension/tsconfig.jsonnew file+21
@@ -0,0 +1,21 @@
1+{
2+ "compilerOptions": {
3+ "module": "commonjs",
4+ "target": "es6",
5+ "outDir": "out",
6+ "lib": [
7+ "es6"
8+ ],
9+ "sourceMap": true,
10+ "rootDir": "src",
11+ "strict": true /* enable all strict type-checking options */
12+ /* Additional Checks */
13+ // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
14+ // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
15+ // "noUnusedParameters": true, /* Report errors on unused parameters. */
16+ },
17+ "exclude": [
18+ "node_modules",
19+ ".vscode-test"
20+ ]
21+}
extension/tslint.jsonnew file+15
@@ -0,0 +1,15 @@
1+{
2+ "rules": {
3+ "no-string-throw": true,
4+ "no-unused-expression": true,
5+ "no-duplicate-variable": true,
6+ "curly": true,
7+ "class-name": true,
8+ "semicolon": [
9+ true,
10+ "always"
11+ ],
12+ "triple-equals": true
13+ },
14+ "defaultSeverity": "warning"
15+}
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
haskell-ide-core.cabaldeleted−118
@@ -1,118 +0,0 @@
1−cabal-version: >= 1.18
2−build-type: Simple
3−name: haskell-ide-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− base == 4.*,
26− binary,
27− bytestring,
28− containers,
29− deepseq,
30− directory,
31− either,
32− extra,
33− filepath,
34− ghc,
35− ghc-boot-th,
36− ghc-boot,
37− hashable,
38− haskell-lsp,
39− haskell-lsp-types,
40− mtl,
41− pretty,
42− safe-exceptions,
43− shake,
44− stm,
45− syb,
46− text,
47− time,
48− prettyprinter,
49− prettyprinter-ansi-terminal,
50− transformers,
51− uniplate,
52− unordered-containers,
53− uri-encode
54−
55− cpp-options: -DGHC_STABLE
56− default-extensions:
57− LambdaCase
58− BangPatterns
59− DeriveGeneric
60− RecordWildCards
61− ScopedTypeVariables
62− NamedFieldPuns
63− TupleSections
64− TypeApplications
65− ViewPatterns
66− GeneralizedNewtypeDeriving
67− StandaloneDeriving
68−
69− hs-source-dirs:
70− src
71− ../../libs-haskell/prettyprinter-syntax/src
72− exposed-modules:
73− Development.IDE.Logger
74− Development.IDE.UtilGHC
75− Development.IDE.Functions.AtPoint
76− Development.IDE.Functions.Compile
77− Development.IDE.Functions.CPP
78− Development.IDE.Orphans
79− Development.IDE.Functions.DependencyInformation
80− Development.IDE.Functions.Documentation
81− Development.IDE.Functions.FindImports
82− Development.IDE.Functions.GHCError
83− Development.IDE.Functions.SpanInfo
84− Development.IDE.Functions.Warnings
85− Development.IDE.State.FileStore
86− Development.IDE.State.Rules
87− Development.IDE.Compat
88− Development.IDE.Types.Options
89− Development.IDE.State.RuleTypes
90− Development.IDE.State.Service
91− Development.IDE.State.Shake
92− Development.IDE.Types.Diagnostics
93− Development.IDE.Types.Location
94− Development.IDE.Types.LSP
95− Development.IDE.Types.SpanInfo
96− other-modules:
97− Data.Text.Prettyprint.Doc.Syntax
98−
99−executable ide-demo
100− default-language: Haskell2010
101− main-is: Demo.hs
102− ghc-options: -main-is Demo.main
103− build-depends:
104− base == 4.*,
105− filepath,
106− containers,
107− shake,
108− ghc-paths,
109− ghc,
110− extra,
111− text,
112− haskell-ide-core
113−
114− default-extensions:
115− TupleSections
116− RecordWildCards
117−
118− hs-source-dirs: test
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.hs+11−30
@@ -1,38 +1,19 @@
1−-- Copyright (c) 2019 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
2−-- SPDX-License-Identifier: Apache-2.0
3−
41{-# LANGUAGE CPP #-}
5−
6−-- | Attempt at hiding the GHC version differences we can.
7−module Development.IDE.Compat(
8− HieFile(..),
9− mkHieFile,
10− writeHieFile,
11− readHieFile
2+module Development.IDE.Compat
3+ (
4+ getProcessID
125 ) where
136
14−#ifndef GHC_STABLE
15−import HieBin
16−import HieAst
17−import HieTypes
18−#else
19−
20−import GHC
21−import GhcPlugins
22−import NameCache
23−import Avail
24−import TcRnTypes
7+#ifdef mingw32_HOST_OS
258
9+import qualified System.Win32.Process as P (getCurrentProcessId)
10+getProcessID :: IO Int
11+getProcessID = fromIntegral <$> P.getCurrentProcessId
2612
27−mkHieFile :: ModSummary -> TcGblEnv -> RenamedSource -> Hsc HieFile
28−mkHieFile _ _ _ = return (HieFile () [])
29−
30−writeHieFile :: FilePath -> HieFile -> IO ()
31−writeHieFile _ _ = return ()
32−
33−readHieFile :: NameCache -> FilePath -> IO (HieFile, ())
34−readHieFile _ _ = return (HieFile () [], ())
13+#else
3514
36−data HieFile = HieFile {hie_module :: (), hie_exports :: [AvailInfo]}
15+import qualified System.Posix.Process as P (getProcessID)
16+getProcessID :: IO Int
17+getProcessID = fromIntegral <$> P.getProcessID
3718
3819#endif
src/Development/IDE/Core/Compile.hsnew file+598
@@ -0,0 +1,598 @@
1+-- Copyright (c) 2019 The DAML Authors. All rights reserved.
2+-- SPDX-License-Identifier: Apache-2.0
3+
4+{-# LANGUAGE RankNTypes #-}
5+{-# LANGUAGE CPP #-}
6+#include "ghc-api-version.h"
7+
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.
10+module Development.IDE.Core.Compile
11+ ( TcModuleResult(..)
12+ , RunSimplifier(..)
13+ , compileModule
14+ , parseModule
15+ , typecheckModule
16+ , computePackageDeps
17+ , addRelativeImport
18+ , mkTcModuleResult
19+ , generateByteCode
20+ , generateAndWriteHieFile
21+ , generateAndWriteHiFile
22+ , getModSummaryFromImports
23+ , loadHieFile
24+ , loadInterface
25+ , loadDepModule
26+ , loadModuleHome
27+ ) where
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
33+import Development.IDE.GHC.Warnings
34+import Development.IDE.Types.Diagnostics
35+import Development.IDE.GHC.Orphans()
36+import Development.IDE.GHC.Util
37+import qualified GHC.LanguageExtensions.Type as GHC
38+import Development.IDE.Types.Options
39+import Development.IDE.Types.Location
40+import Outputable
41+
42+#if MIN_GHC_API_VERSION(8,6,0)
43+import DynamicLoading (initializePlugins)
44+#endif
45+
46+import GHC hiding (parseModule, typecheckModule)
47+import qualified Parser
48+import Lexer
49+#if MIN_GHC_API_VERSION(8,10,0)
50+#else
51+import ErrUtils
52+#endif
53+
54+import Finder
55+import qualified Development.IDE.GHC.Compat as GHC
56+import qualified Development.IDE.GHC.Compat as Compat
57+import GhcMonad
58+import GhcPlugins as GHC hiding (fst3, (<>))
59+import qualified HeaderInfo as Hdr
60+import HscMain (hscInteractive, hscSimplify)
61+import LoadIface (readIface)
62+import qualified Maybes
63+import MkIface
64+import NameCache
65+import StringBuffer as SB
66+import TcRnMonad (initIfaceLoad, tcg_th_coreplugins)
67+import TcIface (typecheckIface)
68+import TidyPgm
69+
70+import Control.Exception.Safe
71+import Control.Monad.Extra
72+import Control.Monad.Except
73+import Control.Monad.Trans.Except
74+import Data.Bifunctor (first, second)
75+import qualified Data.Text as T
76+import Data.IORef
77+import Data.List.Extra
78+import Data.Maybe
79+import qualified Data.Map.Strict as Map
80+import System.FilePath
81+import System.Directory
82+import System.IO.Extra
83+import Data.Either.Extra (maybeToEither)
84+import Control.DeepSeq (rnf)
85+import Control.Exception (evaluate)
86+import Exception (ExceptionMonad)
87+
88+
89+-- | Given a string buffer, return the string (after preprocessing) and the 'ParsedModule'.
90+parseModule
91+ :: IdeOptions
92+ -> HscEnv
93+ -> [PackageName]
94+ -> FilePath
95+ -> Maybe SB.StringBuffer
96+ -> IO (IdeResult (StringBuffer, ParsedModule))
97+parseModule IdeOptions{..} env comp_pkgs filename mbContents =
98+ fmap (either (, Nothing) id) $
99+ evalGhcEnv env $ runExceptT $ do
100+ (contents, dflags) <- preprocessor filename mbContents
101+ (diag, modu) <- parseFileContents optPreprocessor dflags comp_pkgs filename contents
102+ return (diag, Just (contents, modu))
103+
104+
105+-- | Given a package identifier, what packages does it depend on
106+computePackageDeps
107+ :: HscEnv
108+ -> InstalledUnitId
109+ -> IO (Either [FileDiagnostic] [InstalledUnitId])
110+computePackageDeps env pkg = do
111+ let dflags = hsc_dflags env
112+ case lookupInstalledPackage dflags pkg of
113+ Nothing -> return $ Left [ideErrorText (toNormalizedFilePath' noFilePath) $
114+ T.pack $ "unknown package: " ++ show pkg]
115+ Just pkgInfo -> return $ Right $ depends pkgInfo
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
146+
147+initPlugins :: GhcMonad m => ModSummary -> m ModSummary
148+initPlugins modSummary = do
149+#if MIN_GHC_API_VERSION(8,6,0)
150+ session <- getSession
151+ dflags <- liftIO $ initializePlugins session (ms_hspp_opts modSummary)
152+ return modSummary{ms_hspp_opts = dflags}
153+#else
154+ return modSummary
155+#endif
156+
157+-- | Whether we should run the -O0 simplifier when generating core.
158+--
159+-- This is required for template Haskell to work but we disable this in DAML.
160+-- See #256
161+newtype RunSimplifier = RunSimplifier Bool
162+
163+-- | Compile a single type-checked module to a 'CoreModule' value, or
164+-- provide errors.
165+compileModule
166+ :: RunSimplifier
167+ -> HscEnv
168+ -> [(ModSummary, HomeModInfo)]
169+ -> TcModuleResult
170+ -> IO (IdeResult (SafeHaskellMode, CgGuts, ModDetails))
171+compileModule (RunSimplifier simplify) packageState deps tmr =
172+ fmap (either (, Nothing) (second Just)) $
173+ evalGhcEnv packageState $
174+ catchSrcErrors "compile" $ do
175+ setupEnv (deps ++ [(tmrModSummary tmr, tmrModInfo tmr)])
176+
177+ let tm = tmrModule tmr
178+ session <- getSession
179+ (warnings,desugar) <- withWarnings "compile" $ \tweak -> do
180+ let pm = tm_parsed_module tm
181+ let pm' = pm{pm_mod_summary = tweak $ pm_mod_summary pm}
182+ let tm' = tm{tm_parsed_module = pm'}
183+ GHC.dm_core_module <$> GHC.desugarModule tm'
184+ let tc_result = fst (tm_internals_ (tmrModule tmr))
185+ desugared_guts <-
186+ if simplify
187+ then do
188+ plugins <- liftIO $ readIORef (tcg_th_coreplugins tc_result)
189+ liftIO $ hscSimplify session plugins desugar
190+ else pure desugar
191+ -- give variables unique OccNames
192+ (guts, details) <- liftIO $ tidyProgram session desugared_guts
193+ return (map snd warnings, (mg_safe_haskell desugar, guts, details))
194+
195+generateByteCode :: HscEnv -> [(ModSummary, HomeModInfo)] -> TcModuleResult -> CgGuts -> IO (IdeResult Linkable)
196+generateByteCode hscEnv deps tmr guts =
197+ fmap (either (, Nothing) (second Just)) $
198+ evalGhcEnv hscEnv $
199+ catchSrcErrors "bytecode" $ do
200+ setupEnv (deps ++ [(tmrModSummary tmr, tmrModInfo tmr)])
201+ session <- getSession
202+ (warnings, (_, bytecode, sptEntries)) <- withWarnings "bytecode" $ \tweak ->
203+#if MIN_GHC_API_VERSION(8,10,0)
204+ liftIO $ hscInteractive session guts (GHC.ms_location $ tweak $ GHC.pm_mod_summary $ GHC.tm_parsed_module $ tmrModule tmr)
205+#else
206+ liftIO $ hscInteractive session guts (tweak $ GHC.pm_mod_summary $ GHC.tm_parsed_module $ tmrModule tmr)
207+#endif
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
260+ {importPaths = nubOrd $ maybeToList (moduleImportPath fp modu) ++ importPaths dflags}
261+
262+mkTcModuleResult
263+ :: GhcMonad m
264+ => TypecheckedModule
265+ -> m TcModuleResult
266+mkTcModuleResult tcm = do
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
272+ (iface, _) <- liftIO $ mkIfaceTc session Nothing sf details tcGblEnv
273+#endif
274+ let mod_info = HomeModInfo iface details Nothing
275+ return $ TcModuleResult tcm mod_info
276+ where
277+ (tcGblEnv, details) = tm_internals_ tcm
278+
279+atomicFileWrite :: FilePath -> (FilePath -> IO a) -> IO ()
280+atomicFileWrite targetPath write = do
281+ let dir = takeDirectory targetPath
282+ createDirectoryIfMissing True dir
283+ (tempFilePath, cleanUp) <- newTempFileWithin dir
284+ (write tempFilePath >> renameFile tempFilePath targetPath) `onException` cleanUp
285+
286+generateAndWriteHieFile :: HscEnv -> TypecheckedModule -> IO [FileDiagnostic]
287+generateAndWriteHieFile hscEnv tcm =
288+ handleGenerationErrors dflags "extended interface generation" $ do
289+ case tm_renamed_source tcm of
290+ Just rnsrc -> do
291+ hf <- runHsc hscEnv $
292+ GHC.mkHieFile mod_summary (fst $ tm_internals_ tcm) rnsrc ""
293+ atomicFileWrite targetPath $ flip GHC.writeHieFile hf
294+ _ ->
295+ return ()
296+ where
297+ dflags = hsc_dflags hscEnv
298+ mod_summary = pm_mod_summary $ tm_parsed_module tcm
299+ mod_location = ms_location mod_summary
300+ targetPath = Compat.ml_hie_file mod_location
301+
302+generateAndWriteHiFile :: HscEnv -> TcModuleResult -> IO [FileDiagnostic]
303+generateAndWriteHiFile hscEnv tc =
304+ handleGenerationErrors dflags "interface generation" $ do
305+ atomicFileWrite targetPath $ \fp ->
306+ writeIfaceFile dflags fp modIface
307+ where
308+ modIface = hm_iface $ tmrModInfo tc
309+ modSummary = tmrModSummary tc
310+ targetPath = withBootSuffix $ ml_hi_file $ ms_location $ tmrModSummary tc
311+ withBootSuffix = case ms_hsc_src modSummary of
312+ HsBootFile -> addBootSuffix
313+ _ -> id
314+ dflags = hsc_dflags hscEnv
315+
316+handleGenerationErrors :: DynFlags -> T.Text -> IO () -> IO [FileDiagnostic]
317+handleGenerationErrors dflags source action =
318+ action >> return [] `catches`
319+ [ Handler $ return . diagFromGhcException source dflags
320+ , Handler $ return . diagFromString source DsError (noSpan "<internal>")
321+ . (("Error during " ++ T.unpack source) ++) . show @SomeException
322+ ]
323+
324+
325+-- | Setup the environment that GHC needs according to our
326+-- best understanding (!)
327+--
328+-- This involves setting up the finder cache and populating the
329+-- HPT.
330+setupEnv :: GhcMonad m => [(ModSummary, HomeModInfo)] -> m ()
331+setupEnv tms = do
332+ setupFinderCache (map fst tms)
333+ -- load dependent modules, which must be in topological order.
334+ modifySession $ \e ->
335+ foldl' (\e (_, hmi) -> loadModuleHome hmi e) e tms
336+
337+-- | Initialise the finder cache, dependencies should be topologically
338+-- sorted.
339+setupFinderCache :: GhcMonad m => [ModSummary] -> m ()
340+setupFinderCache mss = do
341+ session <- getSession
342+
343+ -- set the target and module graph in the session
344+ let graph = mkModuleGraph mss
345+ setSession session { hsc_mod_graph = graph }
346+
347+ -- Make modules available for others that import them,
348+ -- by putting them in the finder cache.
349+ let ims = map (InstalledModule (thisInstalledUnitId $ hsc_dflags session) . moduleName . ms_mod) mss
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.
353+ prevFinderCache <- liftIO $ readIORef $ hsc_FC session
354+ let newFinderCache =
355+ foldl'
356+ (\fc (im, ifr) -> GHC.extendInstalledModuleEnv fc im ifr) prevFinderCache
357+ $ zip ims ifrs
358+ newFinderCacheVar <- liftIO $ newIORef $! newFinderCache
359+ modifySession $ \s -> s { hsc_FC = newFinderCacheVar }
360+
361+
362+-- | Load a module, quickly. Input doesn't need to be desugared.
363+-- A module must be loaded before dependent modules can be typechecked.
364+-- This variant of loadModuleHome will *never* cause recompilation, it just
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.
370+loadModuleHome
371+ :: HomeModInfo
372+ -> HscEnv
373+ -> HscEnv
374+loadModuleHome mod_info e =
375+ e { hsc_HPT = addToHpt (hsc_HPT e) mod_name mod_info }
376+ where
377+ mod_name = moduleName $ mi_module $ hm_iface mod_info
378+
379+-- | Load module interface.
380+loadDepModuleIO :: ModIface -> Maybe Linkable -> HscEnv -> IO HscEnv
381+loadDepModuleIO iface linkable hsc = do
382+ details <- liftIO $ fixIO $ \details -> do
383+ let hsc' = hsc { hsc_HPT = addToHpt (hsc_HPT hsc) mod (HomeModInfo iface details linkable) }
384+ initIfaceLoad hsc' (typecheckIface iface)
385+ let mod_info = HomeModInfo iface details linkable
386+ return $ loadModuleHome mod_info hsc
387+ where
388+ mod = moduleName $ mi_module iface
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+ ])
414+
415+-- | Produce a module summary from a StringBuffer.
416+getModSummaryFromBuffer
417+ :: GhcMonad m
418+ => FilePath
419+ -> DynFlags
420+ -> GHC.ParsedSource
421+ -> ExceptT [FileDiagnostic] m ModSummary
422+getModSummaryFromBuffer fp dflags parsed = do
423+ (modName, imports) <- liftEither $ getImportsParsed dflags parsed
424+
425+ modLoc <- liftIO $ mkHomeModLocation dflags modName fp
426+ let InstalledUnitId unitId = thisInstalledUnitId dflags
427+ return $ ModSummary
428+ { ms_mod = mkModule (fsToUnitId unitId) modName
429+ , ms_location = modLoc
430+ , ms_hs_date = error "Rules should not depend on ms_hs_date"
431+ -- When we are working with a virtual file we do not have a file date.
432+ -- To avoid silent issues where something is not processed because the date
433+ -- has not changed, we make sure that things blow up if they depend on the
434+ -- date.
435+ , ms_textual_imps = [imp | (False, imp) <- imports]
436+ , ms_hspp_file = fp
437+ , ms_hspp_opts = dflags
438+ , ms_hspp_buf = Nothing
439+
440+ -- defaults:
441+ , ms_hsc_src = sourceType
442+ , ms_obj_date = Nothing
443+ , ms_iface_date = Nothing
444+#if MIN_GHC_API_VERSION(8,8,0)
445+ , ms_hie_date = Nothing
446+#endif
447+ , ms_srcimps = [imp | (True, imp) <- imports]
448+ , ms_parsed_mod = Nothing
449+ }
450+ where
451+ sourceType = if "-boot" `isSuffixOf` takeExtension fp then HsBootFile else HsSrcFile
452+
453+-- | Given a buffer, env and filepath, produce a module summary by parsing only the imports.
454+-- Runs preprocessors as needed.
455+getModSummaryFromImports
456+ :: (HasDynFlags m, ExceptionMonad m, MonadIO m)
457+ => FilePath
458+ -> Maybe SB.StringBuffer
459+ -> ExceptT [FileDiagnostic] m ModSummary
460+getModSummaryFromImports fp contents = do
461+ (contents, dflags) <- preprocessor fp contents
462+ (srcImports, textualImports, L _ moduleName) <-
463+ ExceptT $ liftIO $ first (diagFromErrMsgs "parser" dflags) <$> GHC.getHeaderImports dflags contents fp fp
464+
465+ -- Force bits that might keep the string buffer and DynFlags alive unnecessarily
466+ liftIO $ evaluate $ rnf srcImports
467+ liftIO $ evaluate $ rnf textualImports
468+
469+ modLoc <- liftIO $ mkHomeModLocation dflags moduleName fp
470+
471+ let mod = mkModule (thisPackage dflags) moduleName
472+ sourceType = if "-boot" `isSuffixOf` takeExtension fp then HsBootFile else HsSrcFile
473+ summary =
474+ ModSummary
475+ { ms_mod = mod
476+#if MIN_GHC_API_VERSION(8,8,0)
477+ , ms_hie_date = Nothing
478+#endif
479+ , ms_hs_date = error "Rules should not depend on ms_hs_date"
480+ -- When we are working with a virtual file we do not have a file date.
481+ -- To avoid silent issues where something is not processed because the date
482+ -- has not changed, we make sure that things blow up if they depend on the date.
483+ , ms_hsc_src = sourceType
484+ , ms_hspp_buf = Nothing
485+ , ms_hspp_file = fp
486+ , ms_hspp_opts = dflags
487+ , ms_iface_date = Nothing
488+ , ms_location = modLoc
489+ , ms_obj_date = Nothing
490+ , ms_parsed_mod = Nothing
491+ , ms_srcimps = srcImports
492+ , ms_textual_imps = textualImports
493+ }
494+ return summary
495+
496+
497+-- | Given a buffer, flags, and file path, produce a
498+-- parsed module (or errors) and any parse warnings. Does not run any preprocessors
499+parseFileContents
500+ :: GhcMonad m
501+ => (GHC.ParsedSource -> IdePreprocessedSource)
502+ -> DynFlags -- ^ flags to use
503+ -> [PackageName] -- ^ The package imports to ignore
504+ -> FilePath -- ^ the filename (for source locations)
505+ -> SB.StringBuffer -- ^ Haskell module source text (full Unicode is supported)
506+ -> ExceptT [FileDiagnostic] m ([FileDiagnostic], ParsedModule)
507+parseFileContents customPreprocessor dflags comp_pkgs filename contents = do
508+ let loc = mkRealSrcLoc (mkFastString filename) 1 1
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
513+ PFailed _ locErr msgErr ->
514+ throwE $ diagFromErrMsg "parser" dflags $ mkPlainErrMsg dflags locErr msgErr
515+#endif
516+ POk pst rdr_module ->
517+ let hpm_annotations =
518+ (Map.fromListWith (++) $ annotations pst,
519+ Map.fromList ((noSrcSpan,comment_q pst)
520+ :annotations_comments pst))
521+ (warns, errs) = getMessages pst dflags
522+ in
523+ do
524+ -- Just because we got a `POk`, it doesn't mean there
525+ -- weren't errors! To clarify, the GHC parser
526+ -- distinguishes between fatal and non-fatal
527+ -- errors. Non-fatal errors are the sort that don't
528+ -- prevent parsing from continuing (that is, a parse
529+ -- tree can still be produced despite the error so that
530+ -- further errors/warnings can be collected). Fatal
531+ -- errors are those from which a parse tree just can't
532+ -- be produced.
533+ unless (null errs) $
534+ throwE $ diagFromErrMsgs "parser" dflags $ snd $ getMessages pst dflags
535+
536+ -- Ok, we got here. It's safe to continue.
537+ let IdePreprocessedSource preproc_warns errs parsed = customPreprocessor rdr_module
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
541+ ms <- getModSummaryFromBuffer filename dflags parsed'
542+ let pm =
543+ ParsedModule {
544+ pm_mod_summary = ms
545+ , pm_parsed_source = parsed'
546+ , pm_extra_src_files=[] -- src imports not allowed
547+ , pm_annotations = hpm_annotations
548+ }
549+ warnings = diagFromErrMsgs "parser" dflags warns
550+ pure (warnings ++ preproc_warnings, pm)
551+
552+
553+-- | After parsing the module remove all package imports referring to
554+-- these packages as we have already dealt with what they map to.
555+removePackageImports :: [PackageName] -> GHC.ParsedSource -> GHC.ParsedSource
556+removePackageImports pkgs (L l h@HsModule {hsmodImports} ) = L l (h { hsmodImports = imports' })
557+ where
558+ imports' = map do_one_import hsmodImports
559+ do_one_import (L l i@ImportDecl{ideclPkgQual}) =
560+ case PackageName . sl_fs <$> ideclPkgQual of
561+ Just pn | pn `elem` pkgs -> L l (i { ideclPkgQual = Nothing })
562+ _ -> L l i
563+#if MIN_GHC_API_VERSION(8,6,0)
564+ do_one_import l = l
565+#endif
566+
567+loadHieFile :: FilePath -> IO GHC.HieFile
568+loadHieFile f = do
569+ u <- mkSplitUniqSupply 'a'
570+ let nameCache = initNameCache u []
571+ fmap (GHC.hie_file_result . fst) $ GHC.readHieFile nameCache f
572+
573+-- | Retuns an up-to-date module interface if available.
574+-- Assumes file exists.
575+-- Requires the 'HscEnv' to be set up with dependencies
576+loadInterface
577+ :: HscEnv
578+ -> ModSummary
579+ -> [HiFileResult]
580+ -> IO (Either String ModIface)
581+loadInterface session ms deps = do
582+ let hiFile = case ms_hsc_src ms of
583+ HsBootFile -> addBootSuffix (ml_hi_file $ ms_location ms)
584+ _ -> ml_hi_file $ ms_location ms
585+ r <- initIfaceLoad session $ readIface (ms_mod ms) hiFile
586+ case r of
587+ Maybes.Succeeded iface -> do
588+ session' <- foldM (\e d -> loadDepModuleIO (hirModIface d) Nothing e) session deps
589+ (reason, iface') <- checkOldIface session' ms SourceUnmodified (Just iface)
590+ return $ maybeToEither (showReason reason) iface'
591+ Maybes.Failed err -> do
592+ let errMsg = showSDoc (hsc_dflags session) err
593+ return $ Left errMsg
594+
595+showReason :: RecompileRequired -> String
596+showReason MustCompile = "Stale"
597+showReason (RecompBecause reason) = "Stale (" ++ reason ++ ")"
598+showReason UpToDate = "Up to date"
src/Development/IDE/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.hsnew file+187
@@ -0,0 +1,187 @@
1+-- Copyright (c) 2019 The DAML Authors. All rights reserved.
2+-- SPDX-License-Identifier: Apache-2.0
3+{-# LANGUAGE CPP #-}
4+{-# LANGUAGE TypeFamilies #-}
5+
6+module Development.IDE.Core.FileStore(
7+ getFileContents,
8+ getVirtualFile,
9+ setBufferModified,
10+ setSomethingModified,
11+ fileStoreRules,
12+ VFSHandle,
13+ makeVFSHandle,
14+ makeLSPVFSHandle
15+ ) where
16+
17+import Development.IDE.GHC.Orphans()
18+import Development.IDE.Core.Shake
19+import Control.Concurrent.Extra
20+import qualified Data.Map.Strict as Map
21+import Data.Maybe
22+import qualified Data.Text as T
23+import Control.Monad.Extra
24+import Development.Shake
25+import Development.Shake.Classes
26+import Control.Exception
27+import GHC.Generics
28+import Data.Either.Extra
29+import System.IO.Error
30+import qualified Data.ByteString.Char8 as BS
31+import Development.IDE.Types.Diagnostics
32+import Development.IDE.Types.Location
33+import qualified Data.Rope.UTF16 as Rope
34+
35+#ifdef mingw32_HOST_OS
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
46+
47+import Language.Haskell.LSP.Core
48+import Language.Haskell.LSP.VFS
49+
50+-- | haskell-lsp manages the VFS internally and automatically so we cannot use
51+-- the builtin VFS without spawning up an LSP server. To be able to test things
52+-- like `setBufferModified` we abstract over the VFS implementation.
53+data VFSHandle = VFSHandle
54+ { getVirtualFile :: NormalizedUri -> IO (Maybe VirtualFile)
55+ -- ^ get the contents of a virtual file
56+ , setVirtualFileContents :: Maybe (NormalizedUri -> Maybe T.Text -> IO ())
57+ -- ^ set a specific file to a value. If Nothing then we are ignoring these
58+ -- signals anyway so can just say something was modified
59+ }
60+
61+instance IsIdeGlobal VFSHandle
62+
63+makeVFSHandle :: IO VFSHandle
64+makeVFSHandle = do
65+ vfsVar <- newVar (1, Map.empty)
66+ pure VFSHandle
67+ { getVirtualFile = \uri -> do
68+ (_nextVersion, vfs) <- readVar vfsVar
69+ pure $ Map.lookup uri vfs
70+ , setVirtualFileContents = Just $ \uri content ->
71+ modifyVar_ vfsVar $ \(nextVersion, vfs) -> pure $ (nextVersion + 1, ) $
72+ case content of
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.
75+ Just content -> Map.insert uri (VirtualFile nextVersion 0 (Rope.fromText content)) vfs
76+ }
77+
78+makeLSPVFSHandle :: LspFuncs c -> VFSHandle
79+makeLSPVFSHandle lspFuncs = VFSHandle
80+ { getVirtualFile = getVirtualFileFunc lspFuncs
81+ , setVirtualFileContents = Nothing
82+ }
83+
84+
85+-- | Get the contents of a file, either dirty (if the buffer is modified) or Nothing to mean use from disk.
86+type instance RuleResult GetFileContents = (FileVersion, Maybe T.Text)
87+
88+data GetFileContents = GetFileContents
89+ deriving (Eq, Show, Generic)
90+instance Hashable GetFileContents
91+instance NFData GetFileContents
92+instance Binary GetFileContents
93+
94+getModificationTimeRule :: VFSHandle -> Rules ()
95+getModificationTimeRule vfs =
96+ defineEarlyCutoff $ \GetModificationTime file -> do
97+ let file' = fromNormalizedFilePath file
98+ let wrap time@(l,s) = (Just $ BS.pack $ show time, ([], Just $ ModificationTime l s))
99+ alwaysRerun
100+ mbVirtual <- liftIO $ getVirtualFile vfs $ filePathToUri' file
101+ case mbVirtual of
102+ Just (virtualFileVersion -> ver) ->
103+ pure (Just $ BS.pack $ show ver, ([], Just $ VFSVersion ver))
104+ Nothing -> liftIO $ fmap wrap (getModTime file')
105+ `catch` \(e :: IOException) -> do
106+ let err | isDoesNotExistError e = "File does not exist: " ++ file'
107+ | otherwise = "IO error while reading " ++ file' ++ ", " ++ displayException e
108+ return (Nothing, ([ideErrorText file $ T.pack err], Nothing))
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
139+
140+getFileContentsRule :: VFSHandle -> Rules ()
141+getFileContentsRule vfs =
142+ define $ \GetFileContents file -> do
143+ -- need to depend on modification time to introduce a dependency with Cutoff
144+ time <- use_ GetModificationTime file
145+ res <- liftIO $ ideTryIOException file $ do
146+ mbVirtual <- getVirtualFile vfs $ filePathToUri' file
147+ pure $ Rope.toText . _text <$> mbVirtual
148+ case res of
149+ Left err -> return ([err], Nothing)
150+ Right contents -> return ([], Just (time, contents))
151+
152+ideTryIOException :: NormalizedFilePath -> IO a -> IO (Either FileDiagnostic a)
153+ideTryIOException fp act =
154+ mapLeft
155+ (\(e :: IOException) -> ideErrorText fp $ T.pack $ show e)
156+ <$> try act
157+
158+
159+getFileContents :: NormalizedFilePath -> Action (FileVersion, Maybe T.Text)
160+getFileContents = use_ GetFileContents
161+
162+fileStoreRules :: VFSHandle -> Rules ()
163+fileStoreRules vfs = do
164+ addIdeGlobal vfs
165+ getModificationTimeRule vfs
166+ getFileContentsRule vfs
167+
168+
169+-- | Notify the compiler service that a particular file has been modified.
170+-- Use 'Nothing' to say the file is no longer in the virtual file system
171+-- but should be sourced from disk, or 'Just' to give its new value.
172+setBufferModified :: IdeState -> NormalizedFilePath -> Maybe T.Text -> IO ()
173+setBufferModified state absFile contents = do
174+ VFSHandle{..} <- getIdeGlobalState state
175+ whenJust setVirtualFileContents $ \set ->
176+ set (filePathToUri' absFile) contents
177+ void $ shakeRun state []
178+
179+-- | Note that some buffer somewhere has been modified, but don't say what.
180+-- Only valid if the virtual file system was initialised by LSP, as that
181+-- independently tracks which files are modified.
182+setSomethingModified :: IdeState -> IO ()
183+setSomethingModified state = do
184+ VFSHandle{..} <- getIdeGlobalState state
185+ when (isJust setVirtualFileContents) $
186+ fail "setSomethingModified can't be called on this type of VFSHandle"
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.hsnew file+82
@@ -0,0 +1,82 @@
1+-- Copyright (c) 2019 The DAML Authors. All rights reserved.
2+-- SPDX-License-Identifier: Apache-2.0
3+
4+{-# LANGUAGE TypeFamilies #-}
5+{-# LANGUAGE FlexibleInstances #-}
6+
7+-- | Utilities and state for the files of interest - those which are currently
8+-- open in the editor. The useful function is 'getFilesOfInterest'.
9+module Development.IDE.Core.OfInterest(
10+ ofInterestRules,
11+ getFilesOfInterest, setFilesOfInterest, modifyFilesOfInterest,
12+ ) where
13+
14+import Control.Concurrent.Extra
15+import Data.Binary
16+import Data.Hashable
17+import Control.DeepSeq
18+import GHC.Generics
19+import Data.Typeable
20+import qualified Data.ByteString.UTF8 as BS
21+import Control.Exception
22+import Data.HashSet (HashSet)
23+import qualified Data.HashSet as HashSet
24+import qualified Data.Text as T
25+import Data.Tuple.Extra
26+import Data.Functor
27+import Development.Shake
28+
29+import Development.IDE.Types.Location
30+import Development.IDE.Types.Logger
31+import Development.IDE.Core.Shake
32+
33+
34+newtype OfInterestVar = OfInterestVar (Var (HashSet NormalizedFilePath))
35+instance IsIdeGlobal OfInterestVar
36+
37+type instance RuleResult GetFilesOfInterest = HashSet NormalizedFilePath
38+
39+data GetFilesOfInterest = GetFilesOfInterest
40+ deriving (Eq, Show, Typeable, Generic)
41+instance Hashable GetFilesOfInterest
42+instance NFData GetFilesOfInterest
43+instance Binary GetFilesOfInterest
44+
45+
46+-- | The rule that initialises the files of interest state.
47+ofInterestRules :: Rules ()
48+ofInterestRules = do
49+ addIdeGlobal . OfInterestVar =<< liftIO (newVar HashSet.empty)
50+ defineEarlyCutoff $ \GetFilesOfInterest _file -> assert (null $ fromNormalizedFilePath _file) $ do
51+ alwaysRerun
52+ filesOfInterest <- getFilesOfInterestUntracked
53+ pure (Just $ BS.fromString $ show filesOfInterest, ([], Just filesOfInterest))
54+
55+
56+-- | Get the files that are open in the IDE.
57+getFilesOfInterest :: Action (HashSet NormalizedFilePath)
58+getFilesOfInterest = useNoFile_ GetFilesOfInterest
59+
60+
61+
62+------------------------------------------------------------
63+-- Exposed API
64+
65+-- | Set the files-of-interest - not usually necessary or advisable.
66+-- The LSP client will keep this information up to date.
67+setFilesOfInterest :: IdeState -> HashSet NormalizedFilePath -> IO ()
68+setFilesOfInterest state files = modifyFilesOfInterest state (const files)
69+
70+getFilesOfInterestUntracked :: Action (HashSet NormalizedFilePath)
71+getFilesOfInterestUntracked = do
72+ OfInterestVar var <- getIdeGlobalAction
73+ liftIO $ readVar var
74+
75+-- | Modify the files-of-interest - not usually necessary or advisable.
76+-- The LSP client will keep this information up to date.
77+modifyFilesOfInterest :: IdeState -> (HashSet NormalizedFilePath -> HashSet NormalizedFilePath) -> IO ()
78+modifyFilesOfInterest state f = do
79+ OfInterestVar var <- getIdeGlobalState state
80+ files <- modifyVar var $ pure . dupe . f
81+ logDebug (ideLogger state) $ "Set files of interest to: " <> T.pack (show $ HashSet.toList files)
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/State/RuleTypes.hs → src/Development/IDE/Core/RuleTypes.hsrenamed+92−80
@@ -1,41 +1,38 @@
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.
22-- SPDX-License-Identifier: Apache-2.0
33
44{-# LANGUAGE FlexibleInstances #-}
55{-# LANGUAGE TypeFamilies #-}
6−{-# OPTIONS_GHC -Wno-orphans #-}
76
87-- | A Shake implementation of the compiler service, built
98-- using the "Shaker" abstraction layer for in-memory use.
109--
11−module Development.IDE.State.RuleTypes(
12− module Development.IDE.State.RuleTypes
10+module Development.IDE.Core.RuleTypes(
11+ module Development.IDE.Core.RuleTypes
1312 ) where
1413
1514import Control.DeepSeq
16−import Development.IDE.Functions.Compile (TcModuleResult, GhcModule, LoadPackageResult(..))
17−import qualified Development.IDE.Functions.Compile as Compile
18−import Development.IDE.Functions.FindImports (Import(..))
19−import Development.IDE.Functions.DependencyInformation
15+import Data.Binary
16+import Development.IDE.Import.DependencyInformation
17+import Development.IDE.GHC.Util
2018import Data.Hashable
2119import Data.Typeable
22−import Development.Shake hiding (Env, newCache)
20+import qualified Data.Set as S
21+import Development.Shake
2322import GHC.Generics (Generic)
2423
2524import GHC
26−import Development.IDE.Compat
27−import Module
25+import Module (InstalledUnitId)
26+import HscTypes (CgGuts, Linkable, HomeModInfo, ModDetails)
2827
29−import Development.IDE.Types.SpanInfo
28+import Development.IDE.Spans.Type
29+import Development.IDE.Import.FindImports (ArtifactsLocation)
3030
3131
3232-- NOTATION
3333-- Foo+ means Foo for the dependencies
3434-- Foo* means Foo for me and Foo+
3535
36−-- | Kick off things
37−type instance RuleResult OfInterest = ()
38−
3936-- | The parse tree for the file using GetFileContents
4037type instance RuleResult GetParsedModule = ParsedModule
4138
@@ -48,135 +45,150 @@ type instance RuleResult GetDependencyInformation = DependencyInformation
4845-- This rule is also responsible for calling ReportImportCycles for each file in the transitive closure.
4946type instance RuleResult GetDependencies = TransitiveDependencies
5047
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+
5176-- | The type checked version of this file, requires TypeCheck+
5277type instance RuleResult TypeCheck = TcModuleResult
5378
5479-- | Information about what spans occur where, requires TypeCheck
55−type instance RuleResult GetSpanInfo = [SpanInfo]
80+type instance RuleResult GetSpanInfo = SpansInfo
5681
5782-- | Convert to Core, requires TypeCheck*
58−type instance RuleResult GenerateCore = GhcModule
83+type instance RuleResult GenerateCore = (SafeHaskellMode, CgGuts, ModDetails)
84+
85+-- | Generate byte code for template haskell.
86+type instance RuleResult GenerateByteCode = Linkable
5987
6088-- | A GHC session that we reuse.
61−type instance RuleResult GhcSession = HscEnv
89+type instance RuleResult GhcSession = HscEnvEq
6290
63−-- | Resolve the imports in a module to the list of either external packages or absolute file paths
64−-- for modules in the same package.
65−type instance RuleResult GetLocatedImports = [(Located ModuleName, Maybe Import)]
91+-- | Resolve the imports in a module to the file path of a module
92+-- in the same package or the package id of another package.
93+type instance RuleResult GetLocatedImports = ([(Located ModuleName, Maybe ArtifactsLocation)], S.Set InstalledUnitId)
6694
6795-- | This rule is used to report import cycles. It depends on GetDependencyInformation.
6896-- We cannot report the cycles directly from GetDependencyInformation since
6997-- we can only report diagnostics for the current file.
7098type instance RuleResult ReportImportCycles = ()
7199
72−-- | Read the given HIE file.
73−type instance RuleResult GetHieFile = HieFile
100+-- | Read the module interface file
101+type instance RuleResult GetHiFile = HiFileResult
74102
103+-- | Get a module interface, either from an interface file or a typechecked module
104+type instance RuleResult GetModIface = HiFileResult
75105
76−data OfInterest = OfInterest
77− deriving (Eq, Show, Typeable, Generic)
78−instance Hashable OfInterest
79−instance NFData OfInterest
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
80111
81112data GetParsedModule = GetParsedModule
82113 deriving (Eq, Show, Typeable, Generic)
83114instance Hashable GetParsedModule
84115instance NFData GetParsedModule
116+instance Binary GetParsedModule
85117
86118data GetLocatedImports = GetLocatedImports
87119 deriving (Eq, Show, Typeable, Generic)
88120instance Hashable GetLocatedImports
89121instance NFData GetLocatedImports
122+instance Binary GetLocatedImports
90123
91124data GetDependencyInformation = GetDependencyInformation
92125 deriving (Eq, Show, Typeable, Generic)
93126instance Hashable GetDependencyInformation
94127instance NFData GetDependencyInformation
128+instance Binary GetDependencyInformation
95129
96130data ReportImportCycles = ReportImportCycles
97131 deriving (Eq, Show, Typeable, Generic)
98132instance Hashable ReportImportCycles
99133instance NFData ReportImportCycles
134+instance Binary ReportImportCycles
100135
101136data GetDependencies = GetDependencies
102137 deriving (Eq, Show, Typeable, Generic)
103138instance Hashable GetDependencies
104139instance NFData GetDependencies
140+instance Binary GetDependencies
105141
106142data TypeCheck = TypeCheck
107143 deriving (Eq, Show, Typeable, Generic)
108144instance Hashable TypeCheck
109145instance NFData TypeCheck
146+instance Binary TypeCheck
110147
111148data GetSpanInfo = GetSpanInfo
112149 deriving (Eq, Show, Typeable, Generic)
113150instance Hashable GetSpanInfo
114151instance NFData GetSpanInfo
152+instance Binary GetSpanInfo
115153
116154data GenerateCore = GenerateCore
117155 deriving (Eq, Show, Typeable, Generic)
118156instance Hashable GenerateCore
119157instance 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
120165
121166data GhcSession = GhcSession
122167 deriving (Eq, Show, Typeable, Generic)
123168instance Hashable GhcSession
124169instance NFData GhcSession
170+instance Binary GhcSession
125171
126−-- Note that we embed the filepath here instead of using the filepath associated with Shake keys.
127−-- Otherwise we will garbage collect the result since files in package dependencies will not be declared reachable.
128−data GetHieFile = GetHieFile FilePath
172+data GetHiFile = GetHiFile
129173 deriving (Eq, Show, Typeable, Generic)
130−instance Hashable GetHieFile
131−instance NFData GetHieFile
132−
133−------------------------------------------------------------
134−-- Orphan Instances
135−
136−instance NFData (GenLocated SrcSpan ModuleName) where
137− rnf = rwhnf
138−
139−instance Show TcModuleResult where
140− show = show . pm_mod_summary . tm_parsed_module . Compile.tmrModule
141−
142−instance NFData TcModuleResult where
143− rnf = rwhnf
144−
145−instance Show ModSummary where
146− show = show . ms_mod
147−
148−instance Show ParsedModule where
149− show = show . pm_mod_summary
150−
151−instance NFData ModSummary where
152− rnf = rwhnf
153−
154−instance Show HscEnv where
155− show _ = "HscEnv"
156−
157−instance NFData HscEnv where
158− rnf = rwhnf
159−
160−instance NFData ParsedModule where
161− rnf = rwhnf
174+instance Hashable GetHiFile
175+instance NFData GetHiFile
176+instance Binary GetHiFile
162177
163−instance NFData SpanInfo where
164− rnf = rwhnf
165−
166−instance NFData Import where
167− rnf = rwhnf
168−
169−instance Hashable InstalledUnitId where
170− hashWithSalt salt = hashWithSalt salt . installedUnitIdString
171−
172−instance Show LoadPackageResult where
173− show = installedUnitIdString . lprInstalledUnitId
174−
175−instance NFData LoadPackageResult where
176− rnf = rwhnf
178+data GetModIface = GetModIface
179+ deriving (Eq, Show, Typeable, Generic)
180+instance Hashable GetModIface
181+instance NFData GetModIface
182+instance Binary GetModIface
177183
178−instance Show HieFile where
179− show = show . hie_module
184+data IsFileOfInterest = IsFileOfInterest
185+ deriving (Eq, Show, Typeable, Generic)
186+instance Hashable IsFileOfInterest
187+instance NFData IsFileOfInterest
188+instance Binary IsFileOfInterest
180189
181−instance NFData HieFile where
182− rnf = rwhnf
190+data GetModSummary = GetModSummary
191+ deriving (Eq, Show, Typeable, Generic)
192+instance Hashable GetModSummary
193+instance NFData GetModSummary
194+instance Binary GetModSummary
src/Development/IDE/Core/Rules.hsnew file+671
@@ -0,0 +1,671 @@
1+-- Copyright (c) 2019 The DAML Authors. All rights reserved.
2+-- SPDX-License-Identifier: Apache-2.0
3+
4+{-# LANGUAGE CPP #-}
5+{-# LANGUAGE TypeFamilies #-}
6+{-# LANGUAGE FlexibleInstances #-}
7+{-# LANGUAGE DuplicateRecordFields #-}
8+#include "ghc-api-version.h"
9+
10+-- | A Shake implementation of the compiler service, built
11+-- using the "Shaker" abstraction layer for in-memory use.
12+--
13+module Development.IDE.Core.Rules(
14+ IdeState, GetDependencies(..), GetParsedModule(..), TransitiveDependencies(..),
15+ Priority(..), GhcSessionIO(..), GhcSessionFun(..),
16+ priorityTypeCheck,
17+ priorityGenerateCore,
18+ priorityFilesOfInterest,
19+ runAction, useE, useNoFileE, usesE,
20+ toIdeResult, defineNoFile,
21+ mainRule,
22+ getAtPoint,
23+ getDefinition,
24+ getDependencies,
25+ getParsedModule,
26+ generateCore,
27+ ) where
28+
29+import Fingerprint
30+
31+import Data.Binary hiding (get, put)
32+import Data.Bifunctor (first, second)
33+import Control.Monad.Extra
34+import Control.Monad.Trans.Class
35+import Control.Monad.Trans.Maybe
36+import Development.IDE.Core.Compile
37+import Development.IDE.Core.OfInterest
38+import Development.IDE.Types.Options
39+import Development.IDE.Spans.Calculate
40+import Development.IDE.Import.DependencyInformation
41+import Development.IDE.Import.FindImports
42+import Development.IDE.Core.FileExists
43+import Development.IDE.Core.FileStore (getFileContents)
44+import Development.IDE.Types.Diagnostics
45+import Development.IDE.Types.Location
46+import Development.IDE.GHC.Compat hiding (parseModule, typecheckModule)
47+import Development.IDE.GHC.Util
48+import Development.IDE.GHC.WithDynFlags
49+import Data.Either.Extra
50+import Data.Maybe
51+import Data.Foldable
52+import qualified Data.IntMap.Strict as IntMap
53+import Data.IntMap.Strict (IntMap)
54+import Data.List
55+import Data.Ord
56+import qualified Data.Set as Set
57+import qualified Data.Text as T
58+import Development.IDE.GHC.Error
59+import Development.Shake hiding (Diagnostic)
60+import Development.IDE.Core.RuleTypes
61+import Development.IDE.Spans.Type
62+import qualified Data.ByteString.Char8 as BS
63+
64+import qualified GHC.LanguageExtensions as LangExt
65+import HscTypes
66+import PackageConfig
67+import DynFlags (gopt_set, xopt)
68+import GHC.Generics(Generic)
69+
70+import qualified Development.IDE.Spans.AtPoint as AtPoint
71+import Development.IDE.Core.Service
72+import 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
79+
80+-- | This is useful for rules to convert rules that can only produce errors or
81+-- a result into the more general IdeResult type that supports producing
82+-- warnings while also producing a result.
83+toIdeResult :: Either [FileDiagnostic] v -> IdeResult v
84+toIdeResult = either (, Nothing) (([],) . Just)
85+
86+-- | useE is useful to implement functions that aren’t rules but need shortcircuiting
87+-- e.g. getDefinition.
88+useE :: IdeRule k v => k -> NormalizedFilePath -> MaybeT Action v
89+useE k = MaybeT . use k
90+
91+useNoFileE :: IdeRule k v => k -> MaybeT Action v
92+useNoFileE k = useE k emptyFilePath
93+
94+usesE :: IdeRule k v => k -> [NormalizedFilePath] -> MaybeT Action [v]
95+usesE k = MaybeT . fmap sequence . uses k
96+
97+defineNoFile :: IdeRule k v => (k -> Action v) -> Rules ()
98+defineNoFile f = define $ \k file -> do
99+ if file == emptyFilePath then do res <- f k; return ([], Just res) else
100+ fail $ "Rule " ++ show k ++ " should always be called with the empty string for a file"
101+
102+
103+------------------------------------------------------------
104+-- Exposed API
105+
106+-- | Get all transitive file dependencies of a given module.
107+-- Does not include the file itself.
108+getDependencies :: NormalizedFilePath -> Action (Maybe [NormalizedFilePath])
109+getDependencies file = fmap transitiveModuleDeps <$> use GetDependencies file
110+
111+-- | Try to get hover text for the name under point.
112+getAtPoint :: NormalizedFilePath -> Position -> Action (Maybe (Maybe Range, [T.Text]))
113+getAtPoint file pos = fmap join $ runMaybeT $ do
114+ opts <- lift getIdeOptions
115+ spans <- useE GetSpanInfo file
116+ return $ AtPoint.atPoint opts spans pos
117+
118+-- | Goto Definition.
119+getDefinition :: NormalizedFilePath -> Position -> Action (Maybe Location)
120+getDefinition file pos = fmap join $ runMaybeT $ do
121+ opts <- lift getIdeOptions
122+ spans <- useE GetSpanInfo file
123+ lift $ AtPoint.gotoDefinition (getHieFile file) opts (spansExprs spans) pos
124+
125+getHieFile
126+ :: NormalizedFilePath -- ^ file we're editing
127+ -> Module -- ^ module dep we want info for
128+ -> Action (Maybe (HieFile, FilePath)) -- ^ hie stuff for the module
129+getHieFile file mod = do
130+ TransitiveDependencies {transitiveNamedModuleDeps} <- use_ GetDependencies file
131+ case find (\x -> nmdModuleName x == moduleName mod) transitiveNamedModuleDeps of
132+ Just NamedModuleDep{nmdFilePath=nfp} -> do
133+ let modPath = fromNormalizedFilePath nfp
134+ (_diags, hieFile) <- getHomeHieFile nfp
135+ return $ (, modPath) <$> hieFile
136+ _ -> getPackageHieFile mod file
137+
138+
139+getHomeHieFile :: NormalizedFilePath -> Action ([a], Maybe HieFile)
140+getHomeHieFile f = do
141+ ms <- use_ GetModSummary f
142+ let normal_hie_f = toNormalizedFilePath' hie_f
143+ hie_f = ml_hie_file $ ms_location ms
144+ mbHieTimestamp <- use GetModificationTime normal_hie_f
145+ srcTimestamp <- use_ GetModificationTime f
146+ let isUpToDate
147+ | Just d <- mbHieTimestamp = comparing modificationTime d srcTimestamp == GT
148+ | otherwise = False
149+
150+ unless isUpToDate $
151+ void $ use_ TypeCheck f
152+
153+ hf <- liftIO $ whenMaybe isUpToDate (loadHieFile hie_f)
154+ return ([], hf)
155+
156+getPackageHieFile :: Module -- ^ Package Module to load .hie file for
157+ -> NormalizedFilePath -- ^ Path of home module importing the package module
158+ -> Action (Maybe (HieFile, FilePath))
159+getPackageHieFile mod file = do
160+ pkgState <- hscEnv <$> use_ GhcSession file
161+ IdeOptions {..} <- getIdeOptions
162+ let unitId = moduleUnitId mod
163+ case lookupPackageConfig unitId pkgState of
164+ Just pkgConfig -> do
165+ -- 'optLocateHieFile' returns Nothing if the file does not exist
166+ hieFile <- liftIO $ optLocateHieFile optPkgLocationOpts pkgConfig mod
167+ path <- liftIO $ optLocateSrcFile optPkgLocationOpts pkgConfig mod
168+ case (hieFile, path) of
169+ (Just hiePath, Just modPath) ->
170+ -- deliberately loaded outside the Shake graph
171+ -- to avoid dependencies on non-workspace files
172+ liftIO $ Just . (, modPath) <$> loadHieFile hiePath
173+ _ -> return Nothing
174+ _ -> return Nothing
175+
176+-- | Parse the contents of a daml file.
177+getParsedModule :: NormalizedFilePath -> Action (Maybe ParsedModule)
178+getParsedModule file = use GetParsedModule file
179+
180+------------------------------------------------------------
181+-- Rules
182+-- These typically go from key to value and are oracles.
183+
184+priorityTypeCheck :: Priority
185+priorityTypeCheck = Priority 0
186+
187+priorityGenerateCore :: Priority
188+priorityGenerateCore = Priority (-1)
189+
190+priorityFilesOfInterest :: Priority
191+priorityFilesOfInterest = Priority (-2)
192+
193+getParsedModuleRule :: Rules ()
194+getParsedModuleRule = defineEarlyCutoff $ \GetParsedModule file -> do
195+ sess <- use_ GhcSession file
196+ let hsc = hscEnv sess
197+ -- These packages are used when removing PackageImports from a
198+ -- parsed module
199+ comp_pkgs = mapMaybe (fmap fst . mkImportDirs (hsc_dflags hsc)) (deps sess)
200+ opt <- getIdeOptions
201+ (_, contents) <- getFileContents file
202+
203+ let dflags = hsc_dflags hsc
204+ mainParse = getParsedModuleDefinition hsc opt comp_pkgs file contents
205+
206+ -- Parse again (if necessary) to capture Haddock parse errors
207+ if gopt Opt_Haddock dflags
208+ then
209+ liftIO mainParse
210+ else do
211+ let hscHaddock = hsc{hsc_dflags = gopt_set dflags Opt_Haddock}
212+ haddockParse = do
213+ (_, (!diagsHaddock, _)) <-
214+ getParsedModuleDefinition hscHaddock opt comp_pkgs file contents
215+ return diagsHaddock
216+
217+ ((fingerPrint, (diags, res)), diagsHaddock) <-
218+ -- parse twice, with and without Haddocks, concurrently
219+ -- we cannot ignore Haddock parse errors because files of
220+ -- non-interest are always parsed with Haddocks
221+ liftIO $ concurrently mainParse haddockParse
222+
223+ return (fingerPrint, (mergeDiagnostics diags diagsHaddock, res))
224+
225+getParsedModuleDefinition :: HscEnv -> IdeOptions -> [PackageName] -> NormalizedFilePath -> Maybe T.Text -> IO (Maybe ByteString, ([FileDiagnostic], Maybe ParsedModule))
226+getParsedModuleDefinition packageState opt comp_pkgs file contents = do
227+ (diag, res) <- parseModule opt packageState comp_pkgs (fromNormalizedFilePath file) (fmap textToStringBuffer contents)
228+ case res of
229+ Nothing -> pure (Nothing, (diag, Nothing))
230+ Just (contents, modu) -> do
231+ mbFingerprint <- if isNothing $ optShakeFiles opt
232+ then pure Nothing
233+ else Just . fingerprintToBS <$> fingerprintFromStringBuffer contents
234+ pure (mbFingerprint, (diag, Just modu))
235+
236+getLocatedImportsRule :: Rules ()
237+getLocatedImportsRule =
238+ define $ \GetLocatedImports file -> do
239+ ms <- use_ GetModSummary file
240+ let imports = [(False, imp) | imp <- ms_textual_imps ms] ++ [(True, imp) | imp <- ms_srcimps ms]
241+ env_eq <- use_ GhcSession file
242+ let env = hscEnv env_eq
243+ let import_dirs = deps env_eq
244+ let dflags = addRelativeImport file (moduleName $ ms_mod ms) $ hsc_dflags env
245+ opt <- getIdeOptions
246+ (diags, imports') <- fmap unzip $ forM imports $ \(isSource, (mbPkgName, modName)) -> do
247+ diagOrImp <- locateModule dflags import_dirs (optExtensions opt) getFileExists modName mbPkgName isSource
248+ case diagOrImp of
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+ )
269+
270+-- | Given a target file path, construct the raw dependency results by following
271+-- imports recursively.
272+rawDependencyInformation :: [NormalizedFilePath] -> Action RawDependencyInformation
273+rawDependencyInformation fs = do
274+ (rdi, ss) <- execRawDepM (mapM_ go fs)
275+ let bm = IntMap.foldrWithKey (updateBootMap rdi) IntMap.empty ss
276+ return (rdi { rawBootMap = bm })
277+ where
278+ go :: NormalizedFilePath -- ^ Current module being processed
279+ -> StateT (RawDependencyInformation, IntMap ArtifactsLocation) Action FilePathId
280+ go f = do
281+ -- First check to see if we have already processed the FilePath
282+ -- If we have, just return its Id but don't update any of the state.
283+ -- Otherwise, we need to process its imports.
284+ checkAlreadyProcessed f $ do
285+ al <- lift $ modSummaryToArtifactsLocation f <$> use_ GetModSummary f
286+ -- Get a fresh FilePathId for the new file
287+ fId <- getFreshFid al
288+ -- Adding an edge to the bootmap so we can make sure to
289+ -- insert boot nodes before the real files.
290+ addBootMap al fId
291+ -- Try to parse the imports of the file
292+ importsOrErr <- lift $ use GetLocatedImports f
293+ case importsOrErr of
294+ Nothing -> do
295+ -- File doesn't parse so add the module as a failure into the
296+ -- dependency information, continue processing the other
297+ -- elements in the queue
298+ modifyRawDepInfo (insertImport fId (Left ModuleParseError))
299+ return fId
300+ Just (modImports, pkgImports) -> do
301+ -- Get NFPs of the imports which have corresponding files
302+ -- Imports either come locally from a file or from a package.
303+ let (no_file, with_file) = splitImports modImports
304+ (mns, ls) = unzip with_file
305+ -- Recursively process all the imports we just learnt about
306+ -- and get back a list of their FilePathIds
307+ fids <- mapM (go . artifactFilePath) ls
308+ -- Associate together the ModuleName with the FilePathId
309+ let moduleImports' = map (,Nothing) no_file ++ zip mns (map Just fids)
310+ -- Insert into the map the information about this modules
311+ -- imports.
312+ modifyRawDepInfo $ insertImport fId (Right $ ModuleImports moduleImports' pkgImports)
313+ return fId
314+
315+
316+ checkAlreadyProcessed :: NormalizedFilePath -> RawDepM FilePathId -> RawDepM FilePathId
317+ checkAlreadyProcessed nfp k = do
318+ (rawDepInfo, _) <- get
319+ maybe k return (lookupPathToId (rawPathIdMap rawDepInfo) nfp)
320+
321+ modifyRawDepInfo :: (RawDependencyInformation -> RawDependencyInformation) -> RawDepM ()
322+ modifyRawDepInfo f = modify (first f)
323+
324+ addBootMap :: ArtifactsLocation -> FilePathId -> RawDepM ()
325+ addBootMap al fId =
326+ modify (\(rd, ss) -> (rd, if isBootLocation al
327+ then IntMap.insert (getFilePathId fId) al ss
328+ else ss))
329+
330+ getFreshFid :: ArtifactsLocation -> RawDepM FilePathId
331+ getFreshFid al = do
332+ (rawDepInfo, ss) <- get
333+ let (fId, path_map) = getPathId al (rawPathIdMap rawDepInfo)
334+ -- Insert the File into the bootmap if it's a boot module
335+ let rawDepInfo' = rawDepInfo { rawPathIdMap = path_map }
336+ put (rawDepInfo', ss)
337+ return fId
338+
339+ -- Split in (package imports, local imports)
340+ splitImports :: [(Located ModuleName, Maybe ArtifactsLocation)]
341+ -> ([Located ModuleName], [(Located ModuleName, ArtifactsLocation)])
342+ splitImports = foldr splitImportsLoop ([],[])
343+
344+ splitImportsLoop (imp, Nothing) (ns, ls) = (imp:ns, ls)
345+ splitImportsLoop (imp, Just artifact) (ns, ls) = (ns, (imp,artifact) : ls)
346+
347+ updateBootMap pm boot_mod_id ArtifactsLocation{..} bm =
348+ if not artifactIsSource
349+ then
350+ let msource_mod_id = lookupPathToId (rawPathIdMap pm) (toNormalizedFilePath' $ dropBootSuffix artifactModLocation)
351+ in case msource_mod_id of
352+ Just source_mod_id -> insertBootId source_mod_id (FilePathId boot_mod_id) bm
353+ Nothing -> bm
354+ else bm
355+
356+ dropBootSuffix :: ModLocation -> FilePath
357+ dropBootSuffix (ModLocation (Just hs_src) _ _) = reverse . drop (length @[] "-boot") . reverse $ hs_src
358+ dropBootSuffix _ = error "dropBootSuffix"
359+
360+getDependencyInformationRule :: Rules ()
361+getDependencyInformationRule =
362+ define $ \GetDependencyInformation file -> do
363+ rawDepInfo <- rawDependencyInformation [file]
364+ pure ([], Just $ processDependencyInformation rawDepInfo)
365+
366+reportImportCyclesRule :: Rules ()
367+reportImportCyclesRule =
368+ define $ \ReportImportCycles file -> fmap (\errs -> if null errs then ([], Just ()) else (errs, Nothing)) $ do
369+ DependencyInformation{..} <- use_ GetDependencyInformation file
370+ let fileId = pathToId depPathIdMap file
371+ case IntMap.lookup (getFilePathId fileId) depErrorNodes of
372+ Nothing -> pure []
373+ Just errs -> do
374+ let cycles = mapMaybe (cycleErrorInFile fileId) (toList errs)
375+ -- Convert cycles of files into cycles of module names
376+ forM cycles $ \(imp, files) -> do
377+ modNames <- forM files $ \fileId -> do
378+ let file = idToPath depPathIdMap fileId
379+ getModuleName file
380+ pure $ toDiag imp $ sort modNames
381+ where cycleErrorInFile f (PartOfCycle imp fs)
382+ | f `elem` fs = Just (imp, fs)
383+ cycleErrorInFile _ _ = Nothing
384+ toDiag imp mods = (fp , ShowDiag , ) $ Diagnostic
385+ { _range = (_range :: Location -> Range) loc
386+ , _severity = Just DsError
387+ , _source = Just "Import cycle detection"
388+ , _message = "Cyclic module dependency between " <> showCycle mods
389+ , _code = Nothing
390+ , _relatedInformation = Nothing
391+ , _tags = Nothing
392+ }
393+ where loc = srcSpanToLocation (getLoc imp)
394+ fp = toNormalizedFilePath' $ srcSpanToFilename (getLoc imp)
395+ getModuleName file = do
396+ ms <- use_ GetModSummary file
397+ pure (moduleNameString . moduleName . ms_mod $ ms)
398+ showCycle mods = T.intercalate ", " (map T.pack mods)
399+
400+-- returns all transitive dependencies in topological order.
401+-- NOTE: result does not include the argument file.
402+getDependenciesRule :: Rules ()
403+getDependenciesRule =
404+ defineEarlyCutoff $ \GetDependencies file -> do
405+ depInfo <- use_ GetDependencyInformation file
406+ let allFiles = reachableModules depInfo
407+ _ <- uses_ ReportImportCycles allFiles
408+ opts <- getIdeOptions
409+ let mbFingerprints = map (fingerprintString . fromNormalizedFilePath) allFiles <$ optShakeFiles opts
410+ return (fingerprintToBS . fingerprintFingerprints <$> mbFingerprints, ([], transitiveDeps depInfo file))
411+
412+-- Source SpanInfo is used by AtPoint and Goto Definition.
413+getSpanInfoRule :: Rules ()
414+getSpanInfoRule =
415+ define $ \GetSpanInfo file -> do
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
430+ (fileImports, _) <- use_ GetLocatedImports file
431+ let imports = second (fmap artifactFilePath) <$> fileImports
432+ x <- liftIO $ getSrcSpanInfos packageState imports tc parsedDeps (map hirModIface ifaces)
433+ return ([], Just x)
434+
435+-- Typechecks a module.
436+typeCheckRule :: Rules ()
437+typeCheckRule = define $ \TypeCheck file -> do
438+ pm <- use_ GetParsedModule file
439+ -- do not generate interface files as this rule is called
440+ -- for files of interest on every keystroke
441+ typeCheckRuleDefinition file pm SkipGenerationOfInterfaceFiles
442+
443+data GenerateInterfaceFiles
444+ = DoGenerateInterfaceFiles
445+ | SkipGenerationOfInterfaceFiles
446+ deriving (Show)
447+
448+-- This is factored out so it can be directly called from the GetModIface
449+-- rule. Directly calling this rule means that on the initial load we can
450+-- garbage collect all the intermediate typechecked modules rather than
451+-- retain the information forever in the shake graph.
452+typeCheckRuleDefinition
453+ :: NormalizedFilePath -- ^ Path to source file
454+ -> ParsedModule
455+ -> GenerateInterfaceFiles -- ^ Should generate .hi and .hie files ?
456+ -> Action (IdeResult TcModuleResult)
457+typeCheckRuleDefinition file pm generateArtifacts = do
458+ deps <- use_ GetDependencies file
459+ hsc <- hscEnv <$> use_ GhcSession file
460+ -- Figure out whether we need TemplateHaskell or QuasiQuotes support
461+ let graph_needs_th_qq = needsTemplateHaskellOrQQ $ hsc_mod_graph hsc
462+ file_uses_th_qq = uses_th_qq $ ms_hspp_opts (pm_mod_summary pm)
463+ any_uses_th_qq = graph_needs_th_qq || file_uses_th_qq
464+ mirs <- uses_ GetModIface (transitiveModuleDeps deps)
465+ bytecodes <- if any_uses_th_qq
466+ then -- If we use TH or QQ, we must obtain the bytecode
467+ fmap Just <$> uses_ GenerateByteCode (transitiveModuleDeps deps)
468+ else
469+ pure $ repeat Nothing
470+
471+ setPriority priorityTypeCheck
472+ IdeOptions { optDefer = defer } <- getIdeOptions
473+
474+ addUsageDependencies $ liftIO $ do
475+ res <- typecheckModule defer hsc (zipWith unpack mirs bytecodes) pm
476+ case res of
477+ (diags, Just (hsc,tcm)) | DoGenerateInterfaceFiles <- generateArtifacts -> do
478+ diagsHie <- generateAndWriteHieFile hsc (tmrModule tcm)
479+ diagsHi <- generateAndWriteHiFile hsc tcm
480+ return (diags <> diagsHi <> diagsHie, Just tcm)
481+ (diags, res) ->
482+ return (diags, snd <$> res)
483+ where
484+ unpack HiFileResult{..} bc = (hirModSummary, (hirModIface, bc))
485+ uses_th_qq dflags =
486+ xopt LangExt.TemplateHaskell dflags || xopt LangExt.QuasiQuotes dflags
487+
488+ addUsageDependencies :: Action (a, Maybe TcModuleResult) -> Action (a, Maybe TcModuleResult)
489+ addUsageDependencies a = do
490+ r@(_, mtc) <- a
491+ forM_ mtc $ \tc -> do
492+ let used_files = mapMaybe udep (mi_usages (hm_iface (tmrModInfo tc)))
493+ udep (UsageFile fp _h) = Just fp
494+ udep _ = Nothing
495+ -- Add a dependency on these files which are added by things like
496+ -- qAddDependentFile
497+ void $ uses_ GetModificationTime (map toNormalizedFilePath' used_files)
498+ return r
499+
500+
501+generateCore :: RunSimplifier -> NormalizedFilePath -> Action (IdeResult (SafeHaskellMode, CgGuts, ModDetails))
502+generateCore runSimplifier file = do
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
508+
509+generateCoreRule :: Rules ()
510+generateCoreRule =
511+ define $ \GenerateCore -> generateCore (RunSimplifier True)
512+
513+generateByteCodeRule :: Rules ()
514+generateByteCodeRule =
515+ define $ \GenerateByteCode file -> do
516+ deps <- use_ GetDependencies file
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 !_ = ()
535+
536+
537+loadGhcSession :: Rules ()
538+loadGhcSession = do
539+ defineNoFile $ \GhcSessionIO -> do
540+ opts <- getIdeOptions
541+ GhcSessionFun <$> optGhcSession opts
542+ -- This function should always be rerun because it consults a cache to
543+ -- see what HscEnv needs to be used for the file, which can change.
544+ -- However, it should also cut-off early if it's the same HscEnv as
545+ -- last time
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))
654+
655+-- | A rule that wires per-file rules together
656+mainRule :: Rules ()
657+mainRule = do
658+ getParsedModuleRule
659+ getLocatedImportsRule
660+ getDependencyInformationRule
661+ reportImportCyclesRule
662+ getDependenciesRule
663+ typeCheckRule
664+ getSpanInfoRule
665+ generateCoreRule
666+ generateByteCodeRule
667+ loadGhcSession
668+ getHiFileRule
669+ getModIfaceRule
670+ isFileOfInterestRule
671+ getModSummaryRule
src/Development/IDE/Core/Service.hsnew file+107
@@ -0,0 +1,107 @@
1+-- Copyright (c) 2019 The DAML Authors. All rights reserved.
2+-- SPDX-License-Identifier: Apache-2.0
3+
4+{-# LANGUAGE TypeFamilies #-}
5+{-# LANGUAGE FlexibleInstances #-}
6+
7+-- | A Shake implementation of the compiler service, built
8+-- using the "Shaker" abstraction layer for in-memory use.
9+--
10+module Development.IDE.Core.Service(
11+ getIdeOptions,
12+ IdeState, initialise, shutdown,
13+ runAction,
14+ runActionSync,
15+ writeProfile,
16+ getDiagnostics, unsafeClearDiagnostics,
17+ ideLogger,
18+ updatePositionMapping,
19+ ) where
20+
21+import Control.Concurrent.Extra
22+import Control.Concurrent.Async
23+import Data.Maybe
24+import Development.IDE.Types.Options (IdeOptions(..))
25+import Control.Monad
26+import Development.IDE.Core.Debouncer
27+import Development.IDE.Core.FileStore (VFSHandle, fileStoreRules)
28+import Development.IDE.Core.FileExists (fileExistsRules)
29+import Development.IDE.Core.OfInterest
30+import Development.IDE.Types.Logger
31+import Development.Shake
32+import Data.Either.Extra
33+import 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
36+
37+import Development.IDE.Core.Shake
38+
39+
40+
41+newtype GlobalIdeOptions = GlobalIdeOptions IdeOptions
42+instance IsIdeGlobal GlobalIdeOptions
43+
44+------------------------------------------------------------
45+-- Exposed API
46+
47+-- | Initialise the Compiler Service.
48+initialise :: LSP.ClientCapabilities
49+ -> Rules ()
50+ -> IO LSP.LspId
51+ -> (LSP.FromServerMessage -> IO ())
52+ -> Logger
53+ -> Debouncer LSP.NormalizedUri
54+ -> IdeOptions
55+ -> VFSHandle
56+ -> IO IdeState
57+initialise caps mainRule getLspId toDiags logger debouncer options vfs =
58+ shakeOpen
59+ getLspId
60+ toDiags
61+ logger
62+ debouncer
63+ (optShakeProfiling options)
64+ (optReportProgress options)
65+ shakeOptions
66+ { shakeThreads = optThreads options
67+ , shakeFiles = fromMaybe "/dev/null" (optShakeFiles options)
68+ } $ do
69+ addIdeGlobal $ GlobalIdeOptions options
70+ fileStoreRules vfs
71+ ofInterestRules
72+ fileExistsRules getLspId caps vfs
73+ mainRule
74+
75+writeProfile :: IdeState -> FilePath -> IO ()
76+writeProfile = shakeProfile
77+
78+-- | Shutdown the Compiler Service.
79+shutdown :: IdeState -> IO ()
80+shutdown = shakeShut
81+
82+-- This will return as soon as the result of the action is
83+-- available. There might still be other rules running at this point,
84+-- e.g., the ofInterestRule.
85+runAction :: IdeState -> Action a -> IO a
86+runAction ide action = do
87+ bar <- newBarrier
88+ res <- shakeRun ide [do v <- action; liftIO $ signalBarrier bar v; return v]
89+ -- shakeRun might throw an exception (either through action or a default rule),
90+ -- in which case action may not complete successfully, and signalBarrier might not be called.
91+ -- Therefore we wait for either res (which propagates the exception) or the barrier.
92+ -- Importantly, if the barrier does finish, cancelling res only kills waiting for the result,
93+ -- it doesn't kill the actual work
94+ fmap fromEither $ race (head <$> res) $ waitBarrier bar
95+
96+
97+-- | `runActionSync` is similar to `runAction` but it will
98+-- wait for all rules (so in particular the `ofInterestRule`) to
99+-- finish running. This is mainly useful in tests, where you want
100+-- to wait for all rules to fire so you can check diagnostics.
101+runActionSync :: IdeState -> Action a -> IO a
102+runActionSync s act = fmap head $ join $ shakeRun s [act]
103+
104+getIdeOptions :: Action IdeOptions
105+getIdeOptions = do
106+ GlobalIdeOptions x <- getIdeGlobalAction
107+ return x
src/Development/IDE/Core/Shake.hsnew file+849
@@ -0,0 +1,849 @@
1+-- Copyright (c) 2019 The DAML Authors. All rights reserved.
2+-- SPDX-License-Identifier: Apache-2.0
3+
4+{-# LANGUAGE ExistentialQuantification #-}
5+{-# LANGUAGE TypeFamilies #-}
6+{-# LANGUAGE ConstraintKinds #-}
7+
8+-- | A Shake implementation of the compiler service.
9+--
10+-- There are two primary locations where data lives, and both of
11+-- these contain much the same data:
12+--
13+-- * The Shake database (inside 'shakeDb') stores a map of shake keys
14+-- to shake values. In our case, these are all of type 'Q' to 'A'.
15+-- During a single run all the values in the Shake database are consistent
16+-- so are used in conjunction with each other, e.g. in 'uses'.
17+--
18+-- * The 'Values' type stores a map of keys to values. These values are
19+-- always stored as real Haskell values, whereas Shake serialises all 'A' values
20+-- between runs. To deserialise a Shake value, we just consult Values.
21+module Development.IDE.Core.Shake(
22+ IdeState, shakeExtras,
23+ ShakeExtras(..), getShakeExtras, getShakeExtrasRules,
24+ IdeRule, IdeResult, GetModificationTime(..),
25+ shakeOpen, shakeShut,
26+ shakeRun,
27+ shakeProfile,
28+ use, useWithStale, useNoFile, uses, usesWithStale,
29+ use_, useNoFile_, uses_,
30+ define, defineEarlyCutoff, defineOnDisk, needOnDisk, needOnDisks,
31+ getDiagnostics, unsafeClearDiagnostics,
32+ getHiddenDiagnostics,
33+ IsIdeGlobal, addIdeGlobal, addIdeGlobalExtras, getIdeGlobalState, getIdeGlobalAction,
34+ garbageCollect,
35+ setPriority,
36+ sendEvent,
37+ ideLogger,
38+ actionLogger,
39+ FileVersion(..), modificationTime,
40+ Priority(..),
41+ updatePositionMapping,
42+ deleteValue,
43+ OnDiskRule(..),
44+ ) where
45+
46+import Development.Shake hiding (ShakeValue, doesFileExist)
47+import Development.Shake.Database
48+import Development.Shake.Classes
49+import Development.Shake.Rule
50+import qualified Data.HashMap.Strict as HMap
51+import qualified Data.Map.Strict as Map
52+import qualified Data.ByteString.Char8 as BS
53+import Data.Dynamic
54+import Data.Maybe
55+import Data.Map.Strict (Map)
56+import Data.List.Extra (partition, takeEnd)
57+import qualified Data.Set as Set
58+import 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
64+import Development.IDE.Types.Logger hiding (Priority)
65+import Language.Haskell.LSP.Diagnostics
66+import qualified Data.SortedList as SL
67+import Development.IDE.Types.Diagnostics
68+import Development.IDE.Types.Location
69+import Development.IDE.Types.Options
70+import Control.Concurrent.Async
71+import Control.Concurrent.Extra
72+import Control.Exception.Extra
73+import Control.DeepSeq
74+import System.Time.Extra
75+import Data.Typeable
76+import qualified Language.Haskell.LSP.Messages as LSP
77+import qualified Language.Haskell.LSP.Types as LSP
78+import System.FilePath hiding (makeRelative)
79+import qualified Development.Shake as Shake
80+import Control.Monad.Extra
81+import Data.Time
82+import GHC.Generics
83+import System.IO.Unsafe
84+import Numeric.Extra
85+import Language.Haskell.LSP.Types
86+
87+
88+-- information we stash inside the shakeExtra field
89+data ShakeExtras = ShakeExtras
90+ {eventer :: LSP.FromServerMessage -> IO ()
91+ ,debouncer :: Debouncer NormalizedUri
92+ ,logger :: Logger
93+ ,globals :: Var (HMap.HashMap TypeRep Dynamic)
94+ ,state :: Var Values
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
107+ }
108+
109+getShakeExtras :: Action ShakeExtras
110+getShakeExtras = do
111+ Just x <- getShakeExtra @ShakeExtras
112+ return x
113+
114+getShakeExtrasRules :: Rules ShakeExtras
115+getShakeExtrasRules = do
116+ Just x <- getShakeExtraRules @ShakeExtras
117+ return x
118+
119+class Typeable a => IsIdeGlobal a where
120+
121+addIdeGlobal :: IsIdeGlobal a => a -> Rules ()
122+addIdeGlobal x = do
123+ extras <- getShakeExtrasRules
124+ liftIO $ addIdeGlobalExtras extras x
125+
126+addIdeGlobalExtras :: IsIdeGlobal a => ShakeExtras -> a -> IO ()
127+addIdeGlobalExtras ShakeExtras{globals} x@(typeOf -> ty) =
128+ liftIO $ modifyVar_ globals $ \mp -> case HMap.lookup ty mp of
129+ Just _ -> errorIO $ "Internal error, addIdeGlobalExtras, got the same type twice for " ++ show ty
130+ Nothing -> return $! HMap.insert ty (toDyn x) mp
131+
132+
133+getIdeGlobalExtras :: forall a . IsIdeGlobal a => ShakeExtras -> IO a
134+getIdeGlobalExtras ShakeExtras{globals} = do
135+ let typ = typeRep (Proxy :: Proxy a)
136+ x <- HMap.lookup (typeRep (Proxy :: Proxy a)) <$> readVar globals
137+ case x of
138+ Just x
139+ | Just x <- fromDynamic x -> pure x
140+ | otherwise -> errorIO $ "Internal error, getIdeGlobalExtras, wrong type for " ++ show typ ++ " (got " ++ show (dynTypeRep x) ++ ")"
141+ Nothing -> errorIO $ "Internal error, getIdeGlobalExtras, no entry for " ++ show typ
142+
143+getIdeGlobalAction :: forall a . IsIdeGlobal a => Action a
144+getIdeGlobalAction = liftIO . getIdeGlobalExtras =<< getShakeExtras
145+
146+getIdeGlobalState :: forall a . IsIdeGlobal a => IdeState -> IO a
147+getIdeGlobalState = getIdeGlobalExtras . shakeExtras
148+
149+
150+-- | The state of the all values.
151+type Values = HMap.HashMap (NormalizedFilePath, Key) (Value Dynamic)
152+
153+-- | Key type
154+data Key = forall k . (Typeable k, Hashable k, Eq k, Show k) => Key k
155+
156+instance Show Key where
157+ show (Key k) = show k
158+
159+instance Eq Key where
160+ Key k1 == Key k2 | Just k2' <- cast k2 = k1 == k2'
161+ | otherwise = False
162+
163+instance Hashable Key where
164+ hashWithSalt salt (Key key) = hashWithSalt salt (typeOf key, key)
165+
166+-- | The result of an IDE operation. Warnings and errors are in the Diagnostic,
167+-- and a value is in the Maybe. For operations that throw an error you
168+-- expect a non-empty list of diagnostics, at least one of which is an error,
169+-- and a Nothing. For operations that succeed you expect perhaps some warnings
170+-- and a Just. For operations that depend on other failing operations you may
171+-- get empty diagnostics and a Nothing, to indicate this phase throws no fresh
172+-- errors but still failed.
173+--
174+
175+data Value 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
216+
217+type IdeRule k v =
218+ ( Shake.RuleResult k ~ v
219+ , Shake.ShakeValue k
220+ , Show v
221+ , Typeable v
222+ , NFData v
223+ )
224+
225+-- | A Shake database plus persistent store. Can be thought of as storing
226+-- mappings from @(FilePath, k)@ to @RuleResult k@.
227+data IdeState = IdeState
228+ {shakeDb :: ShakeDatabase
229+ ,shakeAbort :: MVar (IO ()) -- close whoever was running last
230+ ,shakeClose :: IO ()
231+ ,shakeExtras :: ShakeExtras
232+ ,shakeProfileDir :: Maybe FilePath
233+ }
234+
235+
236+-- This is debugging code that generates a series of profiles, if the Boolean is true
237+shakeRunDatabaseProfile :: Maybe FilePath -> ShakeDatabase -> [Action a] -> IO ([a], Maybe FilePath)
238+shakeRunDatabaseProfile mbProfileDir shakeDb acts = do
239+ (time, (res,_)) <- duration $ shakeRunDatabase shakeDb acts
240+ proFile <- for mbProfileDir $ \dir -> do
241+ count <- modifyVar profileCounter $ \x -> let !y = x+1 in return (y,y)
242+ let file = "ide-" ++ profileStartTime ++ "-" ++ takeEnd 5 ("0000" ++ show count) ++ "-" ++ showDP 2 time <.> "html"
243+ shakeProfileDatabase shakeDb $ dir </> file
244+ return (dir </> file)
245+ return (res, proFile)
246+
247+{-# NOINLINE profileStartTime #-}
248+profileStartTime :: String
249+profileStartTime = unsafePerformIO $ formatTime defaultTimeLocale "%Y%m%d-%H%M%S" <$> getCurrentTime
250+
251+{-# NOINLINE profileCounter #-}
252+profileCounter :: Var Int
253+profileCounter = unsafePerformIO $ newVar 0
254+
255+setValues :: IdeRule k v
256+ => Var Values
257+ -> k
258+ -> NormalizedFilePath
259+ -> Value v
260+ -> IO ()
261+setValues state key file val = modifyVar_ state $ \vals -> do
262+ -- Force to make sure the old HashMap is not retained
263+ evaluate $ HMap.insert (file, Key key) (fmap toDyn val) vals
264+
265+-- | Delete the value stored for a given ide build key
266+deleteValue
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.
276+getValues :: forall k v. IdeRule k v => Var Values -> k -> NormalizedFilePath -> IO (Maybe (Value v))
277+getValues state key file = do
278+ vs <- readVar state
279+ case HMap.lookup (file, Key key) vs of
280+ Nothing -> pure Nothing
281+ Just v -> do
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
296+
297+-- | Open a 'IdeState', should be shut using 'shakeShut'.
298+shakeOpen :: IO LSP.LspId
299+ -> (LSP.FromServerMessage -> IO ()) -- ^ diagnostic handler
300+ -> Logger
301+ -> Debouncer NormalizedUri
302+ -> Maybe FilePath
303+ -> IdeReportProgress
304+ -> ShakeOptions
305+ -> Rules ()
306+ -> IO IdeState
307+shakeOpen getLspId eventer logger debouncer shakeProfileDir (IdeReportProgress reportProgress) opts rules = do
308+ inProgress <- newVar HMap.empty
309+ shakeExtras <- do
310+ globals <- newVar HMap.empty
311+ state <- newVar HMap.empty
312+ diagnostics <- newVar mempty
313+ hiddenDiagnostics <- newVar mempty
314+ publishedDiagnostics <- newVar mempty
315+ positionMapping <- newVar HMap.empty
316+ pure ShakeExtras{..}
317+ (shakeDb, shakeClose) <-
318+ shakeOpenDatabase
319+ opts
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
326+ shakeAbort <- newMVar $ return ()
327+ shakeDb <- shakeDb
328+ return IdeState{..}
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+
378+shakeProfile :: IdeState -> FilePath -> IO ()
379+shakeProfile IdeState{..} = shakeProfileDatabase shakeDb
380+
381+shakeShut :: IdeState -> IO ()
382+shakeShut IdeState{..} = withMVar shakeAbort $ \stop -> do
383+ -- Shake gets unhappy if you try to close when there is a running
384+ -- request so we first abort that.
385+ stop
386+ shakeClose
387+
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+
398+-- | Spawn immediately. If you are already inside a call to shakeRun that will be aborted with an exception.
399+shakeRun :: IdeState -> [Action a] -> IO (IO [a])
400+shakeRun IdeState{shakeExtras=ShakeExtras{..}, ..} acts =
401+ withMVar'
402+ shakeAbort
403+ (\stop -> do
404+ (stopTime,_) <- duration stop
405+ logDebug logger $ T.pack $ "Starting shakeRun (aborting the previous one took " ++ showDuration stopTime ++ ")"
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
411+ start <- offsetTime
412+ aThread <- asyncWithUnmask $ \restore -> do
413+ res <- try (restore $ shakeRunDatabaseProfile shakeProfileDir shakeDb acts)
414+ runTime <- start
415+ let res' = case res of
416+ Left e -> "exception: " <> displayException e
417+ Right _ -> "completed"
418+ profile = case res of
419+ Right (_, Just fp) ->
420+ let link = case filePathToUri' $ toNormalizedFilePath' fp of
421+ NormalizedUri _ x -> x
422+ in ", profile saved at " <> T.unpack link
423+ _ -> ""
424+ let logMsg = logDebug logger $ T.pack $
425+ "Finishing shakeRun (took " ++ showDuration runTime ++ ", " ++ res' ++ profile ++ ")"
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))
433+
434+getDiagnostics :: IdeState -> IO [FileDiagnostic]
435+getDiagnostics IdeState{shakeExtras = ShakeExtras{diagnostics}} = do
436+ val <- readVar diagnostics
437+ return $ getAllDiagnostics val
438+
439+getHiddenDiagnostics :: IdeState -> IO [FileDiagnostic]
440+getHiddenDiagnostics IdeState{shakeExtras = ShakeExtras{hiddenDiagnostics}} = do
441+ val <- readVar hiddenDiagnostics
442+ return $ getAllDiagnostics val
443+
444+-- | FIXME: This function is temporary! Only required because the files of interest doesn't work
445+unsafeClearDiagnostics :: IdeState -> IO ()
446+unsafeClearDiagnostics IdeState{shakeExtras = ShakeExtras{diagnostics}} =
447+ writeVar diagnostics mempty
448+
449+-- | Clear the results for all files that do not match the given predicate.
450+garbageCollect :: (NormalizedFilePath -> Bool) -> Action ()
451+garbageCollect keep = do
452+ ShakeExtras{state, diagnostics,hiddenDiagnostics,publishedDiagnostics,positionMapping} <- getShakeExtras
453+ liftIO $
454+ do newState <- modifyVar state $ \values -> do
455+ values <- evaluate $ HMap.filterWithKey (\(file, _) _ -> keep file) values
456+ return $! dupe values
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
465+define
466+ :: IdeRule k v
467+ => (k -> NormalizedFilePath -> Action (IdeResult v)) -> Rules ()
468+define op = defineEarlyCutoff $ \k v -> (Nothing,) <$> op k v
469+
470+use :: IdeRule k v
471+ => k -> NormalizedFilePath -> Action (Maybe v)
472+use key file = head <$> uses key [file]
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+
481+use_ :: IdeRule k v => k -> NormalizedFilePath -> Action v
482+use_ key file = head <$> uses_ key [file]
483+
484+useNoFile_ :: IdeRule k v => k -> Action v
485+useNoFile_ key = use_ key emptyFilePath
486+
487+uses_ :: IdeRule k v => k -> [NormalizedFilePath] -> Action [v]
488+uses_ key files = do
489+ res <- uses key files
490+ case sequence res of
491+ Nothing -> liftIO $ throwIO $ BadDependency (show key)
492+ Just v -> return v
493+
494+
495+-- | When we depend on something that reported an error, and we fail as a direct result, throw BadDependency
496+-- which short-circuits the rest of the action
497+data BadDependency = BadDependency String deriving Show
498+instance Exception BadDependency
499+
500+isBadDependency :: SomeException -> Bool
501+isBadDependency x
502+ | Just (x :: ShakeException) <- fromException x = isBadDependency $ shakeExceptionInner x
503+ | Just (_ :: BadDependency) <- fromException x = True
504+ | otherwise = False
505+
506+newtype Q k = Q (k, NormalizedFilePath)
507+ deriving (Eq,Hashable,NFData, Generic)
508+
509+instance Binary k => Binary (Q k)
510+
511+instance Show k => Show (Q k) where
512+ show (Q (k, file)) = show k ++ "; " ++ fromNormalizedFilePath file
513+
514+-- | Invariant: the 'v' must be in normal form (fully evaluated).
515+-- Otherwise we keep repeatedly 'rnf'ing values taken from the Shake database
516+newtype A v = A (Value v)
517+ deriving Show
518+
519+instance NFData (A v) where rnf (A v) = v `seq` ()
520+
521+-- In the Shake database we only store one type of key/result pairs,
522+-- namely Q (question) / A (answer).
523+type instance RuleResult (Q k) = A (RuleResult k)
524+
525+
526+-- | Return up2date results. Stale results will be ignored.
527+uses :: IdeRule k 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
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+
546+
547+defineEarlyCutoff
548+ :: IdeRule k v
549+ => (k -> NormalizedFilePath -> Action (Maybe BS.ByteString, IdeResult v))
550+ -> Rules ()
551+defineEarlyCutoff op = addBuiltinRule noLint noIdentity $ \(Q (key, file)) (old :: Maybe BS.ByteString) mode -> do
552+ extras@ShakeExtras{state, inProgress} <- getShakeExtras
553+ -- don't do progress for GetFileExists, as there are lots of non-nodes for just that one key
554+ (if show key == "GetFileExists" then id else withProgress inProgress file) $ do
555+ val <- case old of
556+ Just old | mode == RunDependenciesSame -> do
557+ v <- liftIO $ getValues state key file
558+ case v of
559+ -- No changes in the dependencies and we have
560+ -- an existing result.
561+ Just v -> return $ Just $ RunResult ChangedNothing old $ A v
562+ _ -> return Nothing
563+ _ -> return Nothing
564+ case val of
565+ Just res -> return res
566+ Nothing -> do
567+ (bs, (diags, res)) <- actionCatch
568+ (do v <- op key file; liftIO $ evaluate $ force v) $
569+ \(e :: SomeException) -> pure (Nothing, ([ideErrorText file $ T.pack $ show e | not $ isBadDependency e],Nothing))
570+ modTime <- liftIO $ (currentValue =<<) <$> getValues state GetModificationTime file
571+ (bs, res) <- case res of
572+ Nothing -> do
573+ staleV <- liftIO $ getValues state key file
574+ pure $ case staleV of
575+ Nothing -> (toShakeValue ShakeResult bs, Failed)
576+ Just v -> case v of
577+ Succeeded ver v -> (toShakeValue ShakeStale bs, Stale ver v)
578+ Stale ver v -> (toShakeValue ShakeStale bs, Stale ver v)
579+ Failed -> (toShakeValue ShakeResult bs, Failed)
580+ Just v -> pure (maybe ShakeNoCutoff ShakeResult bs, Succeeded (vfsVersion =<< modTime) v)
581+ liftIO $ setValues state key file res
582+ updateFileDiagnostics file (Key key) extras $ map (\(_,y,z) -> (y,z)) diags
583+ let eq = case (bs, fmap decodeShakeValue old) of
584+ (ShakeResult a, Just (ShakeResult b)) -> a == b
585+ (ShakeStale a, Just (ShakeStale b)) -> a == b
586+ -- If we do not have a previous result
587+ -- or we got ShakeNoCutoff we always return False.
588+ _ -> False
589+ return $ RunResult
590+ (if eq then ChangedRecomputeSame else ChangedRecomputeDiff)
591+ (encodeShakeValue bs) $
592+ A res
593+
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
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+
699+
700+updateFileDiagnostics ::
701+ NormalizedFilePath
702+ -> Key
703+ -> ShakeExtras
704+ -> [(ShowDiagnostic,Diagnostic)] -- ^ current results
705+ -> Action ()
706+updateFileDiagnostics fp k ShakeExtras{diagnostics, hiddenDiagnostics, publishedDiagnostics, state, debouncer, eventer} current = liftIO $ do
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.
714+ newDiags <- modifyVar diagnostics $ \old -> do
715+ let newDiagsStore = setStageDiagnostics fp (vfsVersion =<< modTime)
716+ (T.pack $ show k) (map snd currentShown) old
717+ let newDiags = getFileDiagnostics fp newDiagsStore
718+ _ <- evaluate newDiagsStore
719+ _ <- evaluate newDiags
720+ pure (newDiagsStore, newDiags)
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) $
734+ eventer $ publishDiagnosticsNotification (fromNormalizedUri uri) newDiags
735+ pure $! HMap.insert uri newDiags published
736+
737+publishDiagnosticsNotification :: Uri -> [Diagnostic] -> LSP.FromServerMessage
738+publishDiagnosticsNotification uri diags =
739+ LSP.NotPublishDiagnostics $
740+ LSP.NotificationMessage "2.0" LSP.TextDocumentPublishDiagnostics $
741+ LSP.PublishDiagnosticsParams uri (List diags)
742+
743+newtype Priority = Priority Double
744+
745+setPriority :: Priority -> Action ()
746+setPriority (Priority p) = reschedule p
747+
748+sendEvent :: LSP.FromServerMessage -> Action ()
749+sendEvent e = do
750+ ShakeExtras{eventer} <- getShakeExtras
751+ liftIO $ eventer e
752+
753+ideLogger :: IdeState -> Logger
754+ideLogger IdeState{shakeExtras=ShakeExtras{logger}} = logger
755+
756+actionLogger :: Action Logger
757+actionLogger = do
758+ ShakeExtras{logger} <- getShakeExtras
759+ return logger
760+
761+
762+data GetModificationTime = GetModificationTime
763+ deriving (Eq, Show, Generic)
764+instance Hashable GetModificationTime
765+instance NFData GetModificationTime
766+instance Binary GetModificationTime
767+
768+-- | Get the modification time of a file.
769+type instance RuleResult GetModificationTime = FileVersion
770+
771+data FileVersion
772+ = VFSVersion !Int
773+ | ModificationTime
774+ !Int -- ^ Large unit (platform dependent, do not make assumptions)
775+ !Int -- ^ Small unit (platform dependent, do not make assumptions)
776+ deriving (Show, Generic)
777+
778+instance NFData FileVersion
779+
780+vfsVersion :: FileVersion -> Maybe Int
781+vfsVersion (VFSVersion i) = Just i
782+vfsVersion ModificationTime{} = Nothing
783+
784+modificationTime :: FileVersion -> Maybe (Int, Int)
785+modificationTime VFSVersion{} = Nothing
786+modificationTime (ModificationTime large small) = Just (large, small)
787+
788+getDiagnosticsFromStore :: StoreItem -> [Diagnostic]
789+getDiagnosticsFromStore (StoreItem _ diags) = concatMap SL.fromSortedList $ Map.elems diags
790+
791+
792+-- | Sets the diagnostics for a file and compilation step
793+-- if you want to clear the diagnostics call this with an empty list
794+setStageDiagnostics
795+ :: NormalizedFilePath
796+ -> TextDocumentVersion -- ^ the time that the file these diagnostics originate from was last edited
797+ -> T.Text
798+ -> [LSP.Diagnostic]
799+ -> DiagnosticStore
800+ -> DiagnosticStore
801+setStageDiagnostics fp timeM stage diags ds =
802+ updateDiagnostics ds uri timeM diagsBySource
803+ where
804+ diagsBySource = Map.singleton (Just stage) (SL.toSortedList diags)
805+ uri = filePathToUri' fp
806+
807+getAllDiagnostics ::
808+ DiagnosticStore ->
809+ [FileDiagnostic]
810+getAllDiagnostics =
811+ concatMap (\(k,v) -> map (fromUri k,ShowDiag,) $ getDiagnosticsFromStore v) . HMap.toList
812+
813+getFileDiagnostics ::
814+ NormalizedFilePath ->
815+ DiagnosticStore ->
816+ [LSP.Diagnostic]
817+getFileDiagnostics fp ds =
818+ maybe [] getDiagnosticsFromStore $
819+ HMap.lookup (filePathToUri' fp) ds
820+
821+filterDiagnostics ::
822+ (NormalizedFilePath -> Bool) ->
823+ DiagnosticStore ->
824+ DiagnosticStore
825+filterDiagnostics keep =
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/Functions/AtPoint.hsdeleted−139
@@ -1,139 +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−-- | Gives information about symbols at a given point in DAML files.
6−-- These are all pure functions that should execute quickly.
7−module Development.IDE.Functions.AtPoint (
8− atPoint
9− , gotoDefinition
10− ) where
11−
12−import Development.IDE.Functions.Documentation
13−import Development.IDE.Functions.GHCError
14−import Development.IDE.Orphans()
15−
16−-- DAML compiler and infrastructure
17−import Development.Shake
18−import Development.IDE.UtilGHC
19−import Development.IDE.Compat
20−import Development.IDE.State.Shake
21−import Development.IDE.State.RuleTypes
22−import Development.IDE.Types.Diagnostics
23−import Development.IDE.Types.LSP
24−import Development.IDE.Types.Options
25−import Development.IDE.Types.SpanInfo as SpanInfo
26−
27−-- GHC API imports
28−import Avail
29−import GHC
30−import DynFlags
31−import FastString
32−import Name
33−import Outputable hiding ((<>))
34−
35−import Control.Monad.Extra
36−import Control.Monad.Trans.Maybe
37−import Data.Maybe
38−import Data.List
39−import qualified Data.Text as T
40−
41−-- | Locate the definition of the name at a given position.
42−gotoDefinition
43− :: IdeOptions
44− -> HscEnv
45− -> [SpanInfo]
46− -> Position
47− -> Action (Maybe Location)
48−gotoDefinition ideOpts pkgState srcSpans pos =
49− listToMaybe <$> locationsAtPoint ideOpts pkgState pos srcSpans
50−
51−-- | Synopsis for the name at a given position.
52−atPoint
53− :: [TypecheckedModule]
54− -> [SpanInfo]
55− -> Position
56− -> Maybe (Maybe Range, [HoverText])
57−atPoint tcs srcSpans pos = do
58− SpanInfo{..} <- listToMaybe $ orderSpans $ spansAtPoint pos srcSpans
59− ty <- spaninfoType
60− let mbName = getNameM spaninfoSource
61− mbDefinedAt = HoverHeading . ("Defined " <>) . T.pack . showSDocUnsafe . pprNameDefnLoc <$> mbName
62− mbDocs = fmap (\name -> getDocumentation name tcs) mbName
63− docInfo = maybe [] (map HoverMarkdown . docHeaders) mbDocs
64− range = Range
65− (Position spaninfoStartLine spaninfoStartCol)
66− (Position spaninfoEndLine spaninfoEndCol)
67− typeSig = HoverDamlCode $ case mbName of
68− Nothing -> ": " <> showName ty
69− Just name ->
70− let modulePrefix = maybe "" (<> ".") (getModuleNameAsText name)
71− in modulePrefix <> showName name <> "\n : " <> showName ty
72− hoverInfo = docInfo <> [typeSig] <> maybeToList mbDefinedAt
73− return (Just range, hoverInfo)
74− where
75− -- NOTE(RJR): This is a bit hacky.
76− -- We don't want to show the user type signatures generated from Eq and Show
77− -- instances, as they do not appear in the source program.
78− -- However the user could have written an `==` or `show` function directly,
79− -- in which case we still want to show information for that.
80− -- Hence we just move such information later in the list of spans.
81− orderSpans :: [SpanInfo] -> [SpanInfo]
82− orderSpans = uncurry (++) . partition (not . isTypeclassDeclSpan)
83− isTypeclassDeclSpan :: SpanInfo -> Bool
84− isTypeclassDeclSpan spanInfo =
85− case getNameM (spaninfoSource spanInfo) of
86− Just name -> any (`isInfixOf` show name) ["==", "showsPrec"]
87− Nothing -> False
88−
89−locationsAtPoint :: IdeOptions -> HscEnv -> Position -> [SpanInfo] -> Action [Location]
90−locationsAtPoint IdeOptions{..} pkgState pos =
91− fmap (map srcSpanToLocation) .
92− mapMaybeM (getSpan . spaninfoSource) .
93− spansAtPoint pos
94− where getSpan :: SpanSource -> Action (Maybe SrcSpan)
95− getSpan NoSource = pure Nothing
96− getSpan (Span sp) = pure $ Just sp
97− getSpan (Named name) = case nameSrcSpan name of
98− sp@(RealSrcSpan _) -> pure $ Just sp
99− UnhelpfulSpan _ -> runMaybeT $ do
100− -- This case usually arises when the definition is in an external package.
101− -- In this case the interface files contain garbage source spans
102− -- so we instead read the .hie files to get useful source spans.
103− let mod = nameModule name
104− let unitId = moduleUnitId mod
105− pkgConfig <- MaybeT $ pure $ lookupPackageConfig unitId pkgState
106− hiePath <- MaybeT $ liftIO $ optLocateHieFile optPkgLocationOpts pkgConfig mod
107− hieFile <- MaybeT $ use (GetHieFile hiePath) ""
108− avail <- MaybeT $ pure $ listToMaybe (filterAvails (eqName name) $ hie_exports hieFile)
109− srcPath <- MaybeT $ liftIO $ optLocateSrcFile optPkgLocationOpts pkgConfig mod
110− -- The location will point to the source file used during compilation.
111− -- This file might no longer exists and even if it does the path will be relative
112− -- to the compilation directory which we don’t know.
113− let span = setFileName srcPath $ nameSrcSpan $ availName avail
114− pure span
115− -- We ignore uniques and source spans and only compare the name and the module.
116− eqName :: Name -> Name -> Bool
117− eqName n n' = nameOccName n == nameOccName n' && nameModule n == nameModule n'
118− setFileName f (RealSrcSpan span) = RealSrcSpan (span { srcSpanFile = mkFastString f })
119− setFileName _ span@(UnhelpfulSpan _) = span
120−
121−spansAtPoint :: Position -> [SpanInfo] -> [SpanInfo]
122−spansAtPoint pos = filter atp where
123− line = _line pos + 1
124− cha = _character pos + 1
125− atp SpanInfo{..} = spaninfoStartLine <= line
126− && spaninfoEndLine >= line
127− && spaninfoStartCol <= cha
128− && spaninfoEndCol >= cha
129−
130−showName :: Outputable a => a -> T.Text
131−showName = T.pack . prettyprint
132− where
133− prettyprint x = renderWithStyle unsafeGlobalDynFlags (ppr x) style
134− style = mkUserStyle unsafeGlobalDynFlags neverQualify AllTheWay
135−
136−getModuleNameAsText :: Name -> Maybe T.Text
137−getModuleNameAsText n = do
138− m <- nameModule_maybe n
139− return . T.pack . moduleNameString $ moduleName m
src/Development/IDE/Functions/Compile.hsdeleted−455
@@ -1,455 +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−{-# LANGUAGE RankNTypes #-}
6−{-# LANGUAGE CPP #-}
7−
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.
10−module Development.IDE.Functions.Compile
11− ( GhcModule(..)
12− , TcModuleResult(..)
13− , LoadPackageResult(..)
14− , getGhcDynFlags
15− , compileModule
16− , getSrcSpanInfos
17− , parseModule
18− , typecheckModule
19− , loadPackage
20− , computePackageDeps
21− ) where
22−
23−import Development.IDE.Functions.Warnings
24−import Development.IDE.Functions.CPP
25−import Development.IDE.Types.Diagnostics
26−import qualified Development.IDE.Functions.FindImports as FindImports
27−import Development.IDE.Functions.GHCError
28−import Development.IDE.Functions.SpanInfo
29−import Development.IDE.UtilGHC
30−import Development.IDE.Compat
31−import Development.IDE.Types.Options
32−
33−import GHC hiding (parseModule, typecheckModule)
34−import qualified Parser
35−import Lexer
36−import Bag
37−
38−import qualified GHC
39−import Panic
40−import GhcMonad
41−import GhcPlugins as GHC hiding (fst3, (<>))
42−import qualified HeaderInfo as Hdr
43−import MkIface
44−import NameCache
45−import StringBuffer as SB
46−import TidyPgm
47−import qualified GHC.LanguageExtensions as LangExt
48−
49−import Control.DeepSeq
50−import Control.Exception as E
51−import Control.Monad
52−import qualified Control.Monad.Trans.Except as Ex
53−import Data.IORef
54−import Data.List.Extra
55−import Data.Maybe
56−import Data.Tuple.Extra
57−import qualified Data.Map.Strict as Map
58−import Data.Time
59−import Development.IDE.Types.SpanInfo
60−import GHC.Generics (Generic)
61−import System.FilePath
62−import System.Directory
63−import System.IO.Extra
64−
65−
66−-- | 'CoreModule' together with some additional information required for the
67−-- conversion to DAML-LF.
68−data GhcModule = GhcModule
69− { gmPath :: Maybe FilePath
70− , gmCore :: CoreModule
71− }
72− deriving (Generic, Show)
73−
74−instance NFData GhcModule
75−
76−-- | Contains the typechecked module and the OrigNameCache entry for
77−-- that module.
78−data TcModuleResult = TcModuleResult
79− { tmrModule :: TypecheckedModule
80− , tmrModInfo :: HomeModInfo
81− , tmrOccEnvName :: OccEnv Name
82− }
83−
84−-- | Contains the result of loading an interface. In particular the delta to the name cache.
85−data LoadPackageResult = LoadPackageResult
86− { lprInstalledUnitId :: InstalledUnitId
87− , lprModuleEnv :: ModuleEnv (OccEnv Name)
88− , lprEps :: ExternalPackageState
89− }
90−
91−-- | Get source span info, used for e.g. AtPoint and Goto Definition.
92−getSrcSpanInfos
93− :: IdeOptions
94− -> ParsedModule
95− -> HscEnv
96− -> [(Located ModuleName, Maybe FilePath)]
97− -> TcModuleResult
98− -> IO [SpanInfo]
99−getSrcSpanInfos opt mod env imports tc =
100− runGhcSession opt (Just mod) env
101− . getSpanInfo imports
102− $ tmrModule tc
103−
104−
105−-- | Given a string buffer, return a pre-processed @ParsedModule@.
106−parseModule
107− :: IdeOptions
108− -> HscEnv
109− -> FilePath
110− -> (UTCTime, SB.StringBuffer)
111− -> IO ([FileDiagnostic], Maybe ParsedModule)
112−parseModule opt@IdeOptions{..} packageState file =
113− fmap (either (, Nothing) (second Just)) . Ex.runExceptT .
114− -- We need packages since imports fail to resolve otherwise.
115− runGhcSessionExcept opt Nothing packageState . parseFileContents optPreprocessor file
116−
117−computePackageDeps ::
118− IdeOptions -> HscEnv -> InstalledUnitId -> IO (Either [FileDiagnostic] [InstalledUnitId])
119−computePackageDeps opts packageState iuid =
120− Ex.runExceptT $
121− runGhcSessionExcept opts Nothing packageState $
122− catchSrcErrors $ do
123− dflags <- hsc_dflags <$> getSession
124− liftIO $ depends <$> getPackage dflags iuid
125−
126−getPackage :: DynFlags -> InstalledUnitId -> IO PackageConfig
127−getPackage dflags p =
128− case lookupInstalledPackage dflags p of
129− Nothing -> E.throwIO $ CmdLineError (missingPackageMsg p)
130− Just pkg -> return pkg
131− where
132− missingPackageMsg p = showSDoc dflags $ text "unknown package:" <+> ppr p
133−
134−-- | Typecheck a single module using the supplied dependencies and packages.
135−typecheckModule
136− :: IdeOptions
137− -> ParsedModule
138− -> HscEnv
139− -> [TcModuleResult]
140− -> ParsedModule
141− -> IO ([FileDiagnostic], Maybe TcModuleResult)
142−typecheckModule opt mod packageState deps pm =
143− fmap (either (, Nothing) (second Just)) $ Ex.runExceptT $
144− runGhcSessionExcept opt (Just mod) packageState $
145− catchSrcErrors $ do
146− setupEnv deps
147− (warnings, tcm) <- withWarnings "Typechecker" $ \tweak ->
148− GHC.typecheckModule pm{pm_mod_summary = tweak $ pm_mod_summary pm}
149− tcm2 <- mkTcModuleResult (WriteInterface $ optWriteIface opt) tcm
150− return (warnings, tcm2)
151−
152−-- | Load a pkg and populate the name cache and external package state.
153−loadPackage ::
154− IdeOptions
155− -> HscEnv
156− -> InstalledUnitId
157− -> IO (Either [FileDiagnostic] LoadPackageResult)
158−loadPackage opt packageState p =
159− Ex.runExceptT $
160− runGhcSessionExcept opt Nothing packageState $
161− catchSrcErrors $ do
162− setupEnv []
163− -- this populates the namecache and external package state
164− session <- getSession
165− modEnv <- nsNames <$> liftIO (readIORef $ hsc_NC session)
166− eps <- liftIO (readIORef $ hsc_EPS session)
167− pure $ LoadPackageResult p modEnv eps
168−
169−-- | Compile a single type-checked module to a 'CoreModule' value, or
170−-- provide errors.
171−compileModule
172− :: IdeOptions
173− -> ParsedModule
174− -> HscEnv
175− -> [TcModuleResult]
176− -> TcModuleResult
177− -> IO ([FileDiagnostic], Maybe GhcModule)
178−compileModule opt mod packageState deps tmr =
179− fmap (either (, Nothing) (second Just)) $ Ex.runExceptT $
180− runGhcSessionExcept opt (Just mod) packageState $
181− catchSrcErrors $ do
182− setupEnv (deps ++ [tmr])
183−
184− let tm = tmrModule tmr
185− session <- getSession
186− (warnings,desugar) <- withWarnings "Desugarer" $ \tweak -> do
187− let pm = tm_parsed_module tm
188− let pm' = pm{pm_mod_summary = tweak $ pm_mod_summary pm}
189− let tm' = tm{tm_parsed_module = pm'}
190− GHC.dm_core_module <$> GHC.desugarModule tm'
191−
192− -- give variables unique OccNames
193− (tidy, details) <- liftIO $ tidyProgram session desugar
194−
195− let path = ml_hs_file $ ms_location $ pm_mod_summary $ tm_parsed_module tm
196− let core = CoreModule
197− (cg_module tidy)
198− (md_types details)
199− (cg_binds tidy)
200− (mg_safe_haskell desugar)
201−
202− return (warnings, GhcModule path core)
203−
204−-- | Evaluate a GHC session using a new environment constructed with
205−-- the supplied options.
206−runGhcSessionExcept
207− :: IdeOptions
208− -> Maybe ParsedModule
209− -> HscEnv
210− -> Ex.ExceptT e Ghc a
211− -> Ex.ExceptT e IO a
212−runGhcSessionExcept opts mbMod pkg m =
213− Ex.ExceptT $ runGhcSession opts mbMod pkg $ Ex.runExceptT m
214−
215−
216−getGhcDynFlags :: IdeOptions -> ParsedModule -> HscEnv -> IO DynFlags
217−getGhcDynFlags opts mod pkg = runGhcSession opts (Just mod) pkg getSessionDynFlags
218−
219−-- | Evaluate a GHC session using a new environment constructed with
220−-- the supplied options.
221−runGhcSession
222− :: IdeOptions
223− -> Maybe ParsedModule
224− -> HscEnv
225− -> Ghc a
226− -> IO a
227−runGhcSession IdeOptions{..} modu env act = runGhcEnv env $ do
228− modifyDynFlags $ \x -> x
229− {importPaths = nubOrd $ maybeToList (moduleImportPaths =<< modu) ++ importPaths x}
230− act
231−
232−
233−moduleImportPaths :: GHC.ParsedModule -> Maybe FilePath
234−moduleImportPaths pm
235− | rootModDir == "." = Just rootPathDir
236− | otherwise = dropTrailingPathSeparator <$> stripSuffix rootModDir rootPathDir
237− where
238− ms = GHC.pm_mod_summary pm
239− file = GHC.ms_hspp_file ms
240− mod' = GHC.ms_mod ms
241− rootPathDir = takeDirectory file
242− rootModDir = takeDirectory . moduleNameSlashes . GHC.moduleName $ mod'
243−
244−
245−newtype WriteInterface = WriteInterface Bool
246−
247−mkTcModuleResult
248− :: GhcMonad m
249− => WriteInterface
250− -> TypecheckedModule
251− -> m TcModuleResult
252−mkTcModuleResult (WriteInterface writeIface) tcm = do
253− session <- getSession
254− nc <- liftIO $ readIORef (hsc_NC session)
255− (iface,_) <- liftIO $ mkIfaceTc session Nothing Sf_None details tcGblEnv
256− liftIO $ when writeIface $ do
257− let path = ".interfaces" </> file tcm
258− createDirectoryIfMissing True (takeDirectory path)
259− writeIfaceFile (hsc_dflags session) (replaceExtension path ".hi") iface
260− -- For now, we write .hie files whenever we write .hi files which roughly corresponds to
261− -- when we are building a package. It should be easily decoupable if that turns out to be
262− -- useful.
263− hieFile <- runHsc session $ mkHieFile (tcModSummary tcm) tcGblEnv (fromJust $ renamedSource tcm)
264− writeHieFile (replaceExtension path ".hie") hieFile
265− let mod_info = HomeModInfo iface details Nothing
266− origNc = nsNames nc
267− case lookupModuleEnv origNc (tcmModule tcm) of
268− Nothing -> panic err
269− Just occ -> return $ TcModuleResult tcm mod_info occ
270− where
271− file = ms_hspp_file . tcModSummary
272− tcmModule = ms_mod . tcModSummary
273− (tcGblEnv, details) = tm_internals_ tcm
274− err = "Internal error : module not found in NameCache :" <>
275− moduleNameString (moduleName $ tcmModule tcm)
276−
277−tcModSummary :: TypecheckedModule -> ModSummary
278−tcModSummary = pm_mod_summary . tm_parsed_module
279−
280−-- | Setup the environment that GHC needs according to our
281−-- best understanding (!)
282−setupEnv :: GhcMonad m => [TcModuleResult] -> m ()
283−setupEnv tms = do
284− session <- getSession
285−
286− let mss = map (pm_mod_summary . tm_parsed_module . tmrModule) tms
287−
288− -- set the target and module graph in the session
289− let graph = mkModuleGraph mss
290− setSession session { hsc_mod_graph = graph }
291−
292− -- Make modules available for others that import them,
293− -- by putting them in the finder cache.
294− let ims = map (InstalledModule (thisInstalledUnitId $ hsc_dflags session) . moduleName . ms_mod) mss
295− ifrs = zipWith (\ms -> InstalledFound (ms_location ms)) mss ims
296− liftIO $ modifyIORef (hsc_FC session) $ \fc ->
297− foldl' (\fc (im, ifr) -> GHC.extendInstalledModuleEnv fc im ifr) fc
298− $ zip ims ifrs
299−
300− -- load dependent modules, which must be in topological order.
301− mapM_ loadModuleHome tms
302−
303−
304−-- | Load a module, quickly. Input doesn't need to be desugared.
305−-- A module must be loaded before dependent modules can be typechecked.
306−-- This variant of loadModuleHome will *never* cause recompilation, it just
307−-- modifies the session.
308−loadModuleHome
309− :: (GhcMonad m)
310− => TcModuleResult
311− -> m ()
312−loadModuleHome tmr = modifySession $ \e ->
313− e { hsc_HPT = addToHpt (hsc_HPT e) mod mod_info }
314− where
315− ms = pm_mod_summary . tm_parsed_module . tmrModule $ tmr
316− mod_info = tmrModInfo tmr
317− mod = ms_mod_name ms
318−
319−-- | Produce a module summary from a StringBuffer.
320−getModSummaryFromBuffer
321− :: GhcMonad m
322− => FilePath
323− -> (SB.StringBuffer, UTCTime)
324− -> DynFlags
325− -> GHC.ParsedSource
326− -> Ex.ExceptT [FileDiagnostic] m ModSummary
327−getModSummaryFromBuffer fp (contents, fileDate) dflags parsed = do
328− (modName, imports) <- FindImports.getImportsParsed dflags parsed
329−
330− let modLoc = ModLocation
331− { ml_hs_file = Just fp
332− , ml_hi_file = replaceExtension fp "hi"
333− , ml_obj_file = replaceExtension fp "o"
334−#ifndef GHC_STABLE
335− , ml_hie_file = replaceExtension fp "hie"
336−#endif
337− -- This does not consider the dflags configuration
338− -- (-osuf and -hisuf, object and hi dir.s).
339− -- However, we anyway don't want to generate them.
340− }
341− InstalledUnitId unitId = thisInstalledUnitId dflags
342− return $ ModSummary
343− { ms_mod = mkModule (fsToUnitId unitId) modName
344− , ms_location = modLoc
345− , ms_hs_date = fileDate
346− , ms_textual_imps = imports
347− , ms_hspp_file = fp
348− , ms_hspp_opts = dflags
349− , ms_hspp_buf = Just contents
350−
351− -- defaults:
352− , ms_hsc_src = HsSrcFile
353− , ms_obj_date = Nothing
354− , ms_iface_date = Nothing
355−#ifndef GHC_STABLE
356− , ms_hie_date = Nothing
357−#endif
358− , ms_srcimps = [] -- source imports are not allowed
359− , ms_parsed_mod = Nothing
360− }
361−
362−-- | Given a buffer, flags, file path and module summary, produce a
363−-- parsed module (or errors) and any parse warnings.
364−parseFileContents
365− :: GhcMonad m
366− => (GHC.ParsedSource -> ([(GHC.SrcSpan, String)], GHC.ParsedSource))
367− -> FilePath -- ^ the filename (for source locations)
368− -> (UTCTime, SB.StringBuffer) -- ^ Haskell module source text (full Unicode is supported)
369− -> Ex.ExceptT [FileDiagnostic] m ([FileDiagnostic], ParsedModule)
370−parseFileContents preprocessor filename (time, contents) = do
371− let loc = mkRealSrcLoc (mkFastString filename) 1 1
372− dflags <- parsePragmasIntoDynFlags filename contents
373−
374− (contents, dflags) <-
375− if not $ xopt LangExt.Cpp dflags then
376− return (contents, dflags)
377− else do
378− contents <- liftIO $ withTempDir $ \dir -> do
379− let inp = dir </> takeFileName filename
380− let out = dir </> takeFileName filename <.> "out"
381− let f x = if SB.atEnd x then Nothing else Just $ SB.nextChar x
382− liftIO $ writeFileUTF8 inp (unfoldr f contents)
383− doCpp dflags True inp out
384− liftIO $ SB.hGetStringBuffer out
385− dflags <- parsePragmasIntoDynFlags filename contents
386− return (contents, dflags)
387−
388− case unP Parser.parseModule (mkPState dflags contents loc) of
389−#ifdef GHC_STABLE
390− PFailed _ locErr msgErr ->
391− Ex.throwE $ mkErrorDoc dflags locErr msgErr
392−#else
393− PFailed s ->
394− -- A fatal parse error was encountered.
395− Ex.throwE $ toDiagnostics dflags $ snd $ getMessages s dflags
396−#endif
397− POk pst rdr_module ->
398− let hpm_annotations =
399− (Map.fromListWith (++) $ annotations pst,
400− Map.fromList ((noSrcSpan,comment_q pst)
401− :annotations_comments pst))
402− (warns, errs) = getMessages pst dflags
403− in
404− do
405− -- Just because we got a `POk`, it doesn't mean there
406− -- weren't errors! To clarify, the GHC parser
407− -- distinguishes between fatal and non-fatal
408− -- errors. Non-fatal errors are the sort that don't
409− -- prevent parsing from continuing (that is, a parse
410− -- tree can still be produced despite the error so that
411− -- further errors/warnings can be collected). Fatal
412− -- errors are those from which a parse tree just can't
413− -- be produced.
414− unless (null errs) $
415− Ex.throwE $ toDiagnostics dflags $ snd $ getMessages pst dflags
416−
417− -- Ok, we got here. It's safe to continue.
418− let (errs, parsed) = preprocessor rdr_module
419− unless (null errs) $ Ex.throwE $ mkErrors dflags errs
420− ms <- getModSummaryFromBuffer filename (contents, time) dflags parsed
421− let pm =
422− ParsedModule {
423− pm_mod_summary = ms
424− , pm_parsed_source = parsed
425− , pm_extra_src_files=[] -- src imports not allowed
426− , pm_annotations = hpm_annotations
427− }
428− warnings = mapMaybe (mkDiag dflags "Parser") $ bagToList warns
429− pure (warnings, pm)
430−
431−
432−-- | This reads the pragma information directly from the provided buffer.
433−parsePragmasIntoDynFlags
434− :: GhcMonad m
435− => FilePath
436− -> SB.StringBuffer
437− -> Ex.ExceptT [FileDiagnostic] m DynFlags
438−parsePragmasIntoDynFlags fp contents = catchSrcErrors $ do
439− dflags0 <- getSessionDynFlags
440− let opts = Hdr.getOptions dflags0 contents fp
441− (dflags, _, _) <- parseDynamicFilePragma dflags0 opts
442− return dflags
443−
444−-- | Run something in a Ghc monad and catch the errors (SourceErrors and
445−-- compiler-internal exceptions like Panic or InstallationError).
446−catchSrcErrors :: GhcMonad m => m a -> Ex.ExceptT [FileDiagnostic] m a
447−catchSrcErrors ghcM = do
448− dflags <- getDynFlags
449− Ex.ExceptT $
450− handleGhcException (ghcExceptionToDiagnostics dflags) $
451− handleSourceError (sourceErrorToDiagnostics dflags) $
452− Right <$> ghcM
453− where
454− ghcExceptionToDiagnostics dflags = return . Left . mkErrorsGhcException dflags
455− sourceErrorToDiagnostics dflags = return . Left . toDiagnostics dflags . srcErrorMessages
src/Development/IDE/Functions/DependencyInformation.hsdeleted−210
@@ -1,210 +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−module Development.IDE.Functions.DependencyInformation
5− ( DependencyInformation(..)
6− , RawDependencyInformation(..)
7− , NodeError(..)
8− , ModuleParseError(..)
9− , TransitiveDependencies(..)
10− , processDependencyInformation
11− , transitiveDeps
12− ) where
13−
14−import Control.DeepSeq
15−import Data.Bifunctor
16−import Development.IDE.Orphans()
17−import Data.Either
18−import Data.Foldable
19−import Data.Graph
20−import Data.List.NonEmpty (NonEmpty(..), nonEmpty)
21−import qualified Data.List.NonEmpty as NonEmpty
22−import Data.Map (Map)
23−import qualified Data.Map.Strict as MS
24−import qualified Data.Map.Lazy as ML
25−import Data.Maybe
26−import Data.Set (Set)
27−import qualified Data.Set as Set
28−import Data.Tuple.Extra (fst3)
29−import GHC.Generics (Generic)
30−
31−import Development.IDE.Types.Diagnostics
32−import Development.IDE.UtilGHC ()
33−
34−import GHC
35−import Module
36−
37−-- | Unprocessed results that we get from following all imports recursively starting from a module.
38−data RawDependencyInformation = RawDependencyInformation
39− { moduleDependencies :: Map FilePath (Either ModuleParseError [(Located ModuleName, Maybe FilePath)])
40− , pkgDependencies :: Map FilePath (Set InstalledUnitId)
41− -- ^ Transitive dependencies on pkgs of this file, i.e. immidiate package dependencies and the
42− -- transitive package dependencies of those packages.
43− }
44−
45−data DependencyInformation =
46− DependencyInformation
47− { depErrorNodes :: Map FilePath (NonEmpty NodeError)
48− -- ^ Nodes that cannot be processed correctly.
49− , depModuleDeps :: Map FilePath (Set FilePath)
50− -- ^ For a non-error node, this contains the set of module immediate dependencies
51− -- in the same package.
52− , depPkgDeps :: Map FilePath (Set InstalledUnitId)
53− -- ^ For a non-error node, this contains the set of immediate pkg deps.
54− } deriving (Show, Generic)
55−
56−instance NFData DependencyInformation
57−
58−-- | This does not contain the actual parse error as that is already reported by GetParsedModule.
59−data ModuleParseError = ModuleParseError
60− deriving (Show, Generic)
61−
62−instance NFData ModuleParseError
63−
64−-- | Error when trying to locate a module.
65−data LocateError = LocateError [Diagnostic]
66− deriving (Eq, Show, Generic)
67−
68−instance NFData LocateError
69−
70−-- | An error attached to a node in the dependency graph.
71−data NodeError
72− = PartOfCycle (Located ModuleName) [FilePath]
73− -- ^ This module is part of an import cycle. The module name corresponds
74− -- to the import that enters the cycle starting from this module.
75− -- The list of filepaths represents the elements
76− -- in the cycle in unspecified order.
77− | FailedToLocateImport (Located ModuleName)
78− -- ^ This module has an import that couldn’t be located.
79− | ParseError ModuleParseError
80− | ParentOfErrorNode (Located ModuleName)
81− -- ^ This module is the parent of a module that cannot be
82− -- processed (either it cannot be parsed, is part of a cycle
83− -- or the parent of another error node).
84− deriving (Show, Generic)
85−
86−instance NFData NodeError where
87− rnf (PartOfCycle m fs) = m `seq` rnf fs
88− rnf (FailedToLocateImport m) = m `seq` ()
89− rnf (ParseError e) = rnf e
90− rnf (ParentOfErrorNode m) = m `seq` ()
91−
92−-- | A processed node in the dependency graph. If there was any error
93−-- during processing the node or any of its dependencies, this is an
94−-- `ErrorNode`. Otherwise it is a `SuccessNode`.
95−data NodeResult
96− = ErrorNode (NonEmpty NodeError)
97− | SuccessNode [(Located ModuleName, FilePath)]
98− deriving Show
99−
100−partitionNodeResults :: [(a, NodeResult)] -> ([(a, NonEmpty NodeError)], [(a, [(Located ModuleName, FilePath)])])
101−partitionNodeResults = partitionEithers . map f
102− where f (a, ErrorNode errs) = Left (a, errs)
103− f (a, SuccessNode imps) = Right (a, imps)
104−
105−instance Semigroup NodeResult where
106− ErrorNode errs <> ErrorNode errs' = ErrorNode (errs <> errs')
107− ErrorNode errs <> SuccessNode _ = ErrorNode errs
108− SuccessNode _ <> ErrorNode errs = ErrorNode errs
109− SuccessNode a <> SuccessNode _ = SuccessNode a
110−
111−processDependencyInformation :: RawDependencyInformation -> DependencyInformation
112−processDependencyInformation rawResults =
113− DependencyInformation
114− { depErrorNodes = MS.mapMaybe errorNode resultGraph
115− , depModuleDeps = moduleDeps
116− , depPkgDeps = pkgDependencies rawResults
117− }
118− where resultGraph = buildResultGraph rawResults
119− successEdges :: [(FilePath, FilePath, [FilePath])]
120− successEdges = map (\(k,ks) -> (k,k,ks)) $ MS.toList $
121− MS.map (map snd) $ MS.mapMaybe successNode resultGraph
122− moduleDeps =
123− MS.fromList $ map (\(_, v, vs) -> (v, Set.fromList vs)) successEdges
124− errorNode (ErrorNode errs) = Just errs
125− errorNode _ = Nothing
126− successNode (SuccessNode fs) = Just fs
127− successNode _ = Nothing
128−
129−-- | Given a dependency graph, buildResultGraph detects and propagates errors in that graph as follows:
130−-- 1. Mark each node that is part of an import cycle as an error node.
131−-- 2. Mark each node that has a parse error as an error node.
132−-- 3. Mark each node whose immediate children could not be located as an error.
133−-- 4. Recursively propagate errors to parents if they are not already error nodes.
134−buildResultGraph :: RawDependencyInformation -> Map FilePath NodeResult
135−buildResultGraph g = propagatedErrors
136− where
137− sccs = stronglyConnComp (graphEdges g)
138− (_, cycles) = partitionSCC sccs
139− cycleErrors :: Map FilePath NodeResult
140− cycleErrors = MS.unionsWith (<>) $ map errorsForCycle cycles
141− errorsForCycle :: [FilePath] -> Map FilePath NodeResult
142− errorsForCycle files =
143− MS.fromListWith (<>) (concatMap (cycleErrorsForFile files) files)
144− cycleErrorsForFile :: [FilePath] -> FilePath -> [(FilePath,NodeResult)]
145− cycleErrorsForFile cycle f =
146− let entryPoints = mapMaybe (findImport f) cycle
147− in map (\imp -> (f, ErrorNode (PartOfCycle imp cycle :| []))) entryPoints
148− otherErrors = MS.map otherErrorsForFile (moduleDependencies g)
149− otherErrorsForFile :: Either ModuleParseError [(Located ModuleName, Maybe FilePath)] -> NodeResult
150− otherErrorsForFile (Left err) = ErrorNode (ParseError err :| [])
151− otherErrorsForFile (Right imports) =
152− let toEither (imp, Nothing) = Left imp
153− toEither (imp, Just path) = Right (imp, path)
154− (errs, imports') = partitionEithers (map toEither imports)
155− in case nonEmpty errs of
156− Nothing -> SuccessNode imports'
157− Just errs' -> ErrorNode (NonEmpty.map FailedToLocateImport errs')
158−
159− unpropagatedErrors = MS.unionWith (<>) cycleErrors otherErrors
160− -- The recursion here is fine since we use a lazy map and
161− -- we only recurse on SuccessNodes. In particular, we do not recurse
162− -- on nodes that are part of a cycle as they are already marked as
163− -- error nodes.
164− propagatedErrors =
165− ML.map propagate unpropagatedErrors
166− propagate :: NodeResult -> NodeResult
167− propagate n@(ErrorNode _) = n
168− propagate n@(SuccessNode imps) =
169− let results = map (\(imp, dep) -> (imp, propagatedErrors MS.! dep)) imps
170− (errs, _) = partitionNodeResults results
171− in case nonEmpty errs of
172− Nothing -> n
173− Just errs' -> ErrorNode (NonEmpty.map (ParentOfErrorNode . fst) errs')
174− findImport :: FilePath -> FilePath -> Maybe (Located ModuleName)
175− findImport file importedFile =
176− case moduleDependencies g MS.! file of
177− Left _ -> error "Tried to call findImport on a module with a parse error"
178− Right imports ->
179− fmap fst $ find (\(_, resolvedImp) -> resolvedImp == Just importedFile) imports
180−
181−graphEdges :: RawDependencyInformation -> [(FilePath, FilePath, [FilePath])]
182−graphEdges g =
183− map (\(k, ks) -> (k, k, ks)) $ MS.toList $ MS.map deps $ moduleDependencies g
184− where deps :: Either e [(i, Maybe FilePath)] -> [FilePath]
185− deps (Left _) = []
186− deps (Right imports) = mapMaybe snd imports
187−
188−partitionSCC :: [SCC a] -> ([a], [[a]])
189−partitionSCC (CyclicSCC xs:rest) = second (xs:) $ partitionSCC rest
190−partitionSCC (AcyclicSCC x:rest) = first (x:) $ partitionSCC rest
191−partitionSCC [] = ([], [])
192−
193−transitiveDeps :: DependencyInformation -> FilePath -> Maybe TransitiveDependencies
194−transitiveDeps DependencyInformation{..} f = do
195− reachableVs <- Set.delete f . Set.fromList . map (fst3 . fromVertex) . reachable g <$> toVertex f
196− let transitiveModuleDeps = filter (\v -> v `Set.member` reachableVs) $ map (fst3 . fromVertex) vs
197− let transitivePkgDeps = Set.toList $ foldMap (\f -> MS.findWithDefault Set.empty f depPkgDeps) (f : transitiveModuleDeps)
198− pure $ TransitiveDependencies {..}
199− where (g, fromVertex, toVertex) = graphFromEdges (map (\(f, fs) -> (f, f, Set.toList fs)) $ MS.toList depModuleDeps)
200− vs = topSort g
201−
202−data TransitiveDependencies = TransitiveDependencies
203− { transitiveModuleDeps :: [FilePath]
204− -- ^ Transitive module dependencies in topological order.
205− -- The module itself is not included.
206− , transitivePkgDeps :: [InstalledUnitId]
207− -- ^ Transitive pkg dependencies in unspecified order.
208− } deriving (Eq, Show, Generic)
209−
210−instance NFData TransitiveDependencies
src/Development/IDE/Functions/FindImports.hsdeleted−148
@@ -1,148 +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−module Development.IDE.Functions.FindImports
7− ( getImportsParsed
8− , locateModule
9− , Import(..)
10− ) where
11−
12−import Development.IDE.Functions.GHCError as ErrUtils
13−import Development.IDE.Orphans()
14−-- GHC imports
15−import BasicTypes (StringLiteral(..))
16−import DynFlags
17−import FastString
18−import GHC
19−import qualified HeaderInfo as Hdr
20−import qualified Module as M
21−import qualified GHC.LanguageExtensions.Type as GHC
22−import Packages
23−import Outputable (showSDoc, ppr, pprPanic)
24−import Finder
25−
26−-- standard imports
27−import Control.Monad.Extra
28−import Control.Monad.IO.Class
29−import qualified Control.Monad.Trans.Except as Ex
30−import System.FilePath
31−
32−data Import
33− = FileImport FilePath
34− | PackageImport M.InstalledUnitId
35− deriving (Show)
36−
37−-- | GhcMonad function to chase imports of a module given as a StringBuffer. Returns given module's
38−-- name and its imports.
39−getImportsParsed :: Monad m =>
40− DynFlags ->
41− GHC.ParsedSource ->
42− Ex.ExceptT [FileDiagnostic] m
43− (M.ModuleName, [(Maybe FastString, Located M.ModuleName)])
44−getImportsParsed dflags (L loc parsed) = do
45− let modName = maybe (GHC.mkModuleName "Main") GHC.unLoc $ GHC.hsmodName parsed
46−
47− -- refuse source imports
48− let srcImports = filter (ideclSource . GHC.unLoc) $ GHC.hsmodImports parsed
49− when (not $ null srcImports) $ Ex.throwE $
50− concat
51− [ mkErrors dflags [(mloc, "Illegal source import of " <> GHC.moduleNameString (GHC.unLoc $ GHC.ideclName i))]
52− | L mloc i <- srcImports ]
53−
54− -- most of these corner cases are also present in https://hackage.haskell.org/package/ghc-8.6.1/docs/src/HeaderInfo.html#getImports
55− -- but we want to avoid parsing the module twice
56− let implicit_prelude = xopt GHC.ImplicitPrelude dflags
57− implicit_imports = Hdr.mkPrelImports modName loc implicit_prelude $ GHC.hsmodImports parsed
58−
59− -- filter out imports that come from packages
60− return (modName, [(fmap sl_fs $ ideclPkgQual i, ideclName i)
61− | i <- map GHC.unLoc $ implicit_imports ++ GHC.hsmodImports parsed
62− , GHC.moduleNameString (GHC.unLoc $ ideclName i) /= "GHC.Prim"
63− ])
64−
65−
66−-- | locate a module in the file system. Where we go from *daml to Haskell
67−locateModuleFile :: MonadIO m
68− => DynFlags
69− -> [String]
70− -> (FilePath -> m Bool)
71− -> ModuleName
72− -> m (Maybe FilePath)
73−locateModuleFile dflags exts doesExist modName = do
74− let candidates = [ prefix </> M.moduleNameSlashes modName <.> ext | prefix <- importPaths dflags, ext <- exts]
75− findM doesExist candidates
76−
77−-- | locate a module in either the file system or the package database. Where we go from *daml to
78−-- Haskell
79−locateModule
80− :: MonadIO m
81− => DynFlags
82− -> [String]
83− -> (FilePath -> m Bool)
84− -> Located ModuleName
85− -> Maybe FastString
86− -> m (Either [FileDiagnostic] Import)
87−locateModule dflags exts doesExist modName mbPkgName = do
88− case mbPkgName of
89− -- if a package name is given we only go look for a package
90− Just _pkgName -> lookupInPackageDB dflags
91− Nothing -> do
92− -- first try to find the module as a file. If we can't find it try to find it in the package
93− -- database.
94− mbFile <- locateModuleFile dflags exts doesExist $ unLoc modName
95− case mbFile of
96− Nothing -> lookupInPackageDB dflags
97− Just file -> return $ Right $ FileImport file
98− where
99− lookupInPackageDB dfs =
100− case lookupModuleWithSuggestions dfs (unLoc modName) mbPkgName of
101− LookupFound _m pkgConfig -> return $ Right $ PackageImport $ unitId pkgConfig
102− reason -> return $ Left $ notFoundErr dfs modName reason
103−
104−-- | Don't call this on a found module.
105−notFoundErr :: DynFlags -> Located M.ModuleName -> LookupResult -> [FileDiagnostic]
106−notFoundErr dfs modName reason =
107− mkError' $ ppr' $ cannotFindModule dfs modName0 $ lookupToFindResult reason
108− where
109− mkError' = mkError dfs (getLoc modName)
110− modName0 = unLoc modName
111− ppr' = showSDoc dfs
112− -- We convert the lookup result to a find result to reuse GHC's cannotFindMoudle pretty printer.
113− lookupToFindResult =
114− \case
115− LookupFound _m _pkgConfig ->
116− pprPanic "Impossible: called lookupToFind on found module." (ppr modName0)
117− LookupMultiple rs -> (FoundMultiple rs)
118− LookupHidden pkg_hiddens mod_hiddens ->
119− (NotFound
120− { fr_paths = []
121− , fr_pkg = Nothing
122− , fr_pkgs_hidden = map (moduleUnitId . fst) pkg_hiddens
123− , fr_mods_hidden = map (moduleUnitId . fst) mod_hiddens
124− , fr_unusables = []
125− , fr_suggestions = []
126− })
127− LookupUnusable unusable ->
128− let unusables' = map get_unusable unusable
129− get_unusable (m, ModUnusable r) = (moduleUnitId m, r)
130− get_unusable (_, r) =
131− pprPanic "findLookupResult: unexpected origin" (ppr r)
132− in (NotFound
133− { fr_paths = []
134− , fr_pkg = Nothing
135− , fr_pkgs_hidden = []
136− , fr_mods_hidden = []
137− , fr_unusables = unusables'
138− , fr_suggestions = []
139− })
140− LookupNotFound suggest ->
141− (NotFound
142− { fr_paths = []
143− , fr_pkg = Nothing
144− , fr_pkgs_hidden = []
145− , fr_mods_hidden = []
146− , fr_unusables = []
147− , fr_suggestions = suggest
148− })
src/Development/IDE/Functions/GHCError.hsdeleted−165
@@ -1,165 +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−{-# LANGUAGE DuplicateRecordFields #-}
4−module Development.IDE.Functions.GHCError
5− ( mkDiag
6− , toDiagnostics
7− , srcSpanToLocation
8− , srcSpanToFilename
9−
10− -- * Producing GHC ErrorMessages
11− , mkErrors
12− , mkError
13− , mkErrorDoc
14− , mkErrorsGhcException
15−
16− -- * Handling errors in the GHC monad (SourceError, ErrorMessages)
17− , Diagnostic
18− , FileDiagnostic
19− , ErrorMessages -- included in module export below
20− , ErrMsg
21− , errMsgSpan
22− , errMsgSeverity
23− , mkPlainErrMsg
24−
25− -- * utilities working with 'ErrMsg' and 'ErrorMessages'
26− , zeroSpan
27− , realSpan
28− , noSpan
29− ) where
30−
31−import Development.IDE.Types.Diagnostics as D
32−import qualified Data.Text as T
33−import Development.IDE.Orphans()
34−import qualified FastString as FS
35−import GHC
36−import Bag
37−import Data.Maybe
38−import ErrUtils
39−import SrcLoc
40−import qualified Outputable as Out
41−import qualified Language.Haskell.LSP.Types as LSP
42−
43−
44−
45−toDiagnostics :: DynFlags -> ErrorMessages -> [FileDiagnostic]
46−toDiagnostics dflags = mapMaybe (mkDiag dflags $ T.pack "Compiler") . bagToList
47−
48−
49−mkDiag :: DynFlags -> T.Text -> ErrMsg -> Maybe FileDiagnostic
50−mkDiag dflags src e =
51− case toDSeverity $ errMsgSeverity e of
52− Nothing -> Nothing
53− Just bSeverity ->
54− Just $ (srcSpanToFilename $ errMsgSpan e,)
55− Diagnostic
56− { _range = srcSpanToRange $ errMsgSpan e
57− , _severity = Just bSeverity
58− , _source = Just src
59− , _message = T.pack $ Out.showSDoc dflags (ErrUtils.pprLocErrMsg e)
60− , _code = Nothing
61− , _relatedInformation = Nothing
62− }
63−
64−-- | Convert a GHC SrcSpan to a DAML compiler Range
65−srcSpanToRange :: SrcSpan -> Range
66−srcSpanToRange (UnhelpfulSpan _) = noRange
67−srcSpanToRange (RealSrcSpan real) = realSrcSpanToRange real
68−
69−realSrcSpanToRange :: RealSrcSpan -> Range
70−realSrcSpanToRange real =
71− Range (Position (srcSpanStartLine real - 1) (srcSpanStartCol real - 1))
72− (Position (srcSpanEndLine real - 1) (srcSpanEndCol real - 1))
73−
74−-- | Extract a file name from a GHC SrcSpan (use message for unhelpful ones)
75−-- FIXME This may not be an _absolute_ file name, needs fixing.
76−srcSpanToFilename :: SrcSpan -> FilePath
77−srcSpanToFilename (UnhelpfulSpan fs) = FS.unpackFS fs
78−srcSpanToFilename (RealSrcSpan real) = FS.unpackFS $ srcSpanFile real
79−
80−srcSpanToLocation :: SrcSpan -> Location
81−srcSpanToLocation src =
82− Location (LSP.filePathToUri $ srcSpanToFilename src) (srcSpanToRange src)
83−
84−-- | Convert a GHC severity to a DAML compiler Severity. Severities below
85−-- "Warning" level are dropped (returning Nothing).
86−toDSeverity :: GHC.Severity -> Maybe D.DiagnosticSeverity
87−toDSeverity SevOutput = Nothing
88−toDSeverity SevInteractive = Nothing
89−toDSeverity SevDump = Nothing
90−toDSeverity SevInfo = Just DsInfo
91−toDSeverity SevWarning = Just DsWarning
92−toDSeverity SevError = Just DsError
93−toDSeverity SevFatal = Just DsError
94−
95−
96−-- | Produce a bag of GHC-style errors (@ErrorMessages@) from the given
97−-- (optional) locations and message strings.
98−mkErrors :: DynFlags -> [(SrcSpan, String)] -> [FileDiagnostic]
99−mkErrors dflags = concatMap (uncurry $ mkError dflags)
100−
101−-- | Produce a GHC-style error from a source span and a message.
102−mkError :: DynFlags -> SrcSpan -> String -> [FileDiagnostic]
103−mkError dflags sp = toDiagnostics dflags . Bag.listToBag . pure . mkPlainErrMsg dflags sp . Out.text
104−
105−-- | Produce a GHC-style error from a source span and a message.
106−mkErrorDoc :: DynFlags -> SrcSpan -> Out.SDoc -> [FileDiagnostic]
107−mkErrorDoc dflags sp = toDiagnostics dflags . Bag.listToBag . pure . mkPlainErrMsg dflags sp
108−
109−
110−-- | Produces an "unhelpful" source span with the given string.
111−noSpan :: String -> SrcSpan
112−noSpan = UnhelpfulSpan . FS.fsLit
113−
114−
115−-- | creates a span with zero length in the filename of the argument passed
116−zeroSpan :: FS.FastString -- ^ file path of span
117− -> RealSrcSpan
118−zeroSpan file = realSrcLocSpan (mkRealSrcLoc file 1 1)
119−
120−realSpan :: SrcSpan
121− -> Maybe RealSrcSpan
122−realSpan = \case
123− RealSrcSpan r -> Just r
124− UnhelpfulSpan _ -> Nothing
125−
126−
127−mkErrorsGhcException :: DynFlags -> GhcException -> [FileDiagnostic]
128−mkErrorsGhcException dflags exc = mkErrors dflags [(noSpan "<Internal>", showGHCE dflags exc)]
129−
130−showGHCE :: DynFlags -> GhcException -> String
131−showGHCE dflags exc = case exc of
132− Signal n
133− -> "Signal: " <> show n
134−
135− Panic s
136− -> unwords ["Compilation Issue:", s, "\n", requestReport]
137− PprPanic s sdoc
138− -> unlines ["Compilation Issue", s,""
139− , Out.showSDoc dflags sdoc
140− , requestReport ]
141−
142− Sorry s
143− -> "Unsupported feature: " <> s
144− PprSorry s sdoc
145− -> unlines ["Unsupported feature: ", s,""
146− , Out.showSDoc dflags sdoc]
147−
148−
149− ---------- errors below should not happen at all --------
150− InstallationError str
151− -> "Installation error: " <> str
152−
153− UsageError str -- should never happen
154− -> unlines ["Unexpected usage error", str]
155−
156− CmdLineError str
157− -> unlines ["Unexpected usage error", str]
158−
159− ProgramError str
160− -> "Program error: " <> str
161− PprProgramError str sdoc ->
162− unlines ["Program error:", str,""
163− , Out.showSDoc dflags sdoc]
164− where
165− requestReport = "Please report this bug to the compiler authors."
src/Development/IDE/Functions/SpanInfo.hsdeleted−134
@@ -1,134 +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−-- ORIGINALLY COPIED FROM https://github.com/commercialhaskell/intero
5−
6−{-# LANGUAGE RankNTypes #-}
7−
8−-- | Get information on modules, identifiers, etc.
9−
10−module Development.IDE.Functions.SpanInfo(getSpanInfo,listifyAllSpans) where
11−
12−import ConLike
13−import Control.Monad
14−import qualified CoreUtils
15−import Data.Data
16−import qualified Data.Generics
17−import Data.List
18−import Data.Maybe
19−import DataCon
20−import Desugar
21−import GHC
22−import GhcMonad
23−import FastString (mkFastString)
24−import Development.IDE.Types.SpanInfo
25−import Development.IDE.Functions.GHCError (zeroSpan)
26−import Prelude hiding (mod)
27−import TcHsSyn
28−import Var
29−
30−-- | Get ALL source spans in the module.
31−getSpanInfo :: GhcMonad m
32− => [(Located ModuleName, Maybe FilePath)] -- ^ imports
33− -> TypecheckedModule
34− -> m [SpanInfo]
35−getSpanInfo mods tcm =
36− do let tcs = tm_typechecked_source tcm
37− bs = listifyAllSpans tcs :: [LHsBind GhcTc]
38− es = listifyAllSpans tcs :: [LHsExpr GhcTc]
39− ps = listifyAllSpans' tcs :: [Pat GhcTc]
40− bts <- mapM (getTypeLHsBind tcm) bs -- binds
41− ets <- mapM (getTypeLHsExpr tcm) es -- expressions
42− pts <- mapM (getTypeLPat tcm) ps -- patterns
43− let imports = importInfo mods
44− let exprs = imports ++ concat bts ++ catMaybes (ets ++ pts)
45− return (mapMaybe toSpanInfo (sortBy cmp exprs))
46− where cmp (_,a,_) (_,b,_)
47− | a `isSubspanOf` b = LT
48− | b `isSubspanOf` a = GT
49− | otherwise = EQ
50−
51−-- | Get the name and type of a binding.
52−getTypeLHsBind :: (GhcMonad m)
53− => TypecheckedModule
54− -> LHsBind GhcTc
55− -> m [(SpanSource, SrcSpan, Maybe Type)]
56−getTypeLHsBind _ (L _spn FunBind{fun_id = pid,fun_matches = MG _ _ _typ}) =
57− return [(Named $ getName (unLoc pid), getLoc pid, Just (varType (unLoc pid)))]
58−getTypeLHsBind _ _ = return []
59−
60−-- | Get the name and type of an expression.
61−getTypeLHsExpr :: (GhcMonad m)
62− => TypecheckedModule
63− -> LHsExpr GhcTc
64− -> m (Maybe (SpanSource, SrcSpan, Maybe Type))
65−getTypeLHsExpr _ e = do
66− hs_env <- getSession
67− (_, mbe) <- liftIO (deSugarExpr hs_env e)
68− return $
69− case mbe of
70− Just expr ->
71− Just (getSpanSource (unLoc e), getLoc e, Just (CoreUtils.exprType expr))
72− Nothing -> Nothing
73− where
74− getSpanSource :: HsExpr GhcTc -> SpanSource
75− getSpanSource (HsVar _ (L _ i)) = Named (getName i)
76− getSpanSource (HsConLikeOut _ (RealDataCon dc)) = Named (dataConName dc)
77− getSpanSource RecordCon {rcon_con_name} = Named (getName rcon_con_name)
78− getSpanSource (HsWrap _ _ xpr) = getSpanSource xpr
79− getSpanSource (HsPar _ xpr) = getSpanSource (unLoc xpr)
80− getSpanSource _ = NoSource
81−
82−-- | Get the name and type of a pattern.
83−getTypeLPat :: (GhcMonad m)
84− => TypecheckedModule
85− -> Pat GhcTc
86− -> m (Maybe (SpanSource, SrcSpan, Maybe Type))
87−getTypeLPat _ pat =
88− let (src, spn) = getSpanSource pat in
89− return $ Just (src, spn, Just (hsPatType pat))
90− where
91− getSpanSource :: Pat GhcTc -> (SpanSource, SrcSpan)
92− getSpanSource (VarPat _ (L spn vid)) = (Named (getName vid), spn)
93− getSpanSource (ConPatOut (L spn (RealDataCon dc)) _ _ _ _ _ _) =
94− (Named (dataConName dc), spn)
95− getSpanSource _ = (NoSource, noSrcSpan)
96−
97−importInfo :: [(Located ModuleName, Maybe FilePath)]
98− -> [(SpanSource, SrcSpan, Maybe Type)]
99−importInfo = mapMaybe (uncurry wrk) where
100− wrk :: Located ModuleName -> Maybe FilePath -> Maybe (SpanSource, SrcSpan, Maybe Type)
101− wrk modName = \case
102− Nothing -> Nothing
103− Just afp -> Just (afpToSpanSource afp, getLoc modName, Nothing)
104−
105− -- TODO make this point to the module name
106− afpToSpanSource :: FilePath -> SpanSource
107− afpToSpanSource afp = Span $ RealSrcSpan $ zeroSpan $ mkFastString afp
108−
109−-- | Get ALL source spans in the source.
110−listifyAllSpans :: Typeable a
111− => TypecheckedSource -> [Located a]
112−listifyAllSpans tcs =
113− Data.Generics.listify p tcs
114− where p (L spn _) = isGoodSrcSpan spn
115−-- This is a version of `listifyAllSpans` specialized on picking out
116−-- patterns. It comes about since GHC now defines `type LPat p = Pat
117−-- p` (no top-level locations).
118−listifyAllSpans' :: Typeable a
119− => TypecheckedSource -> [Pat a]
120−listifyAllSpans' tcs = Data.Generics.listify (const True) tcs
121−
122−
123−-- | Pretty print the types into a 'SpanInfo'.
124−toSpanInfo :: (SpanSource, SrcSpan, Maybe Type) -> Maybe SpanInfo
125−toSpanInfo (name,mspan,typ) =
126− case mspan of
127− RealSrcSpan spn ->
128− Just (SpanInfo (srcSpanStartLine spn)
129− (srcSpanStartCol spn - 1)
130− (srcSpanEndLine spn)
131− (srcSpanEndCol spn - 1)
132− typ
133− name)
134− _ -> Nothing
src/Development/IDE/Functions/CPP.hs → src/Development/IDE/GHC/CPP.hsrenamed+52−10
@@ -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.
22-- SPDX-License-Identifier: Apache-2.0
33
44-- 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+
68{- HLINT ignore -} -- since copied from upstream
79
810{-# LANGUAGE CPP, NamedFieldPuns, NondecreasingIndentation, BangPatterns, MultiWayIf #-}
911{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}
12+#include "ghc-api-version.h"
1013
1114-----------------------------------------------------------------------------
1215--
@@ -16,18 +19,30 @@
1619--
1720-----------------------------------------------------------------------------
1821
19−module Development.IDE.Functions.CPP(doCpp) where
22+module Development.IDE.GHC.CPP(doCpp, addOptP)
23+where
2024
25+import Development.IDE.GHC.Compat
2126import Packages
2227import SysTools
2328import Module
2429import DynFlags
2530import Panic
2631import 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
2741
2842import System.Directory
2943import System.FilePath
3044import Control.Monad
45+import System.Info
3146import Data.List ( intercalate )
3247import Data.Maybe
3348import Data.Version
@@ -49,17 +64,19 @@ doCpp dflags raw input_fn output_fn = do
4964 let verbFlags = getVerbFlags dflags
5065
5166 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
5270 | otherwise = SysTools.runCc
53−#ifndef GHC_STABLE
54− Nothing
5571#endif
5672 dflags (SysTools.Option "-E" : args)
5773
58− let target_defs = [] {-
59− [ "-D" ++ HOST_OS ++ "_BUILD_OS",
60− "-D" ++ HOST_ARCH ++ "_BUILD_ARCH",
61− "-D" ++ TARGET_OS ++ "_HOST_OS",
62− "-D" ++ TARGET_ARCH ++ "_HOST_ARCH" ] -}
74+ let target_defs =
75+ -- NEIL: Patched to use System.Info instead of constants from CPP
76+ [ "-D" ++ os ++ "_BUILD_OS",
77+ "-D" ++ arch ++ "_BUILD_ARCH",
78+ "-D" ++ os ++ "_HOST_OS",
79+ "-D" ++ arch ++ "_HOST_ARCH" ]
6380 -- remember, in code we *compile*, the HOST is the same our TARGET,
6481 -- and BUILD is the same as our HOST.
6582
@@ -131,7 +148,16 @@ getBackendDefs :: DynFlags -> IO [String]
131148getBackendDefs dflags | hscTarget dflags == HscLlvm = do
132149 llvmVer <- figureLlvmVersion dflags
133150 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
134159 Just n -> [ "-D__GLASGOW_HASKELL_LLVM__=" ++ format n ]
160+#endif
135161 _ -> []
136162 where
137163 format (major, minor)
@@ -141,6 +167,22 @@ getBackendDefs dflags | hscTarget dflags == HscLlvm = do
141167getBackendDefs _ =
142168 return []
143169
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+
144186-- ---------------------------------------------------------------------------
145187-- Macros (cribbed from Cabal)
146188
src/Development/IDE/GHC/Compat.hsnew file+309
@@ -0,0 +1,309 @@
1+-- Copyright (c) 2019 The DAML Authors. All rights reserved.
2+-- SPDX-License-Identifier: Apache-2.0
3+
4+{-# LANGUAGE CPP #-}
5+{-# LANGUAGE PatternSynonyms #-}
6+#include "ghc-api-version.h"
7+
8+-- | Attempt at hiding the GHC version differences we can.
9+module Development.IDE.GHC.Compat(
10+ getHeaderImports,
11+ HieFileResult(..),
12+ HieFile,
13+ hieExportNames,
14+ hie_module,
15+ mkHieFile,
16+ writeHieFile,
17+ readHieFile,
18+ supportsHieFiles,
19+ setDefaultHieDir,
20+ dontWriteHieFiles,
21+#if !MIN_GHC_API_VERSION(8,8,0)
22+ ml_hie_file,
23+#endif
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
44+ ) where
45+
46+import 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)
64+
65+#if MIN_GHC_API_VERSION(8,10,0)
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)
72+import HieBin
73+import HieTypes
74+
75+supportsHieFiles :: Bool
76+supportsHieFiles = True
77+
78+hieExportNames :: HieFile -> [(SrcSpan, Name)]
79+hieExportNames = nameListFromAvails . hie_exports
80+
81+#else
82+
83+#if MIN_GHC_API_VERSION(8,6,0)
84+import BinIface
85+import Data.IORef
86+import IfaceEnv
87+#endif
88+
89+import Binary
90+import Control.Exception (catch)
91+import Data.ByteString (ByteString)
92+import GhcPlugins hiding (ModLocation)
93+import NameCache
94+import TcRnTypes
95+import System.IO
96+import Foreign.ForeignPtr
97+import MkIface
98+
99+
100+hPutStringBuffer :: Handle -> StringBuffer -> IO ()
101+hPutStringBuffer hdl (StringBuffer buf len cur)
102+ = withForeignPtr (plusForeignPtr buf cur) $ \ptr ->
103+ hPutBuf hdl ptr len
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+
217+mkHieFile :: ModSummary -> TcGblEnv -> RenamedSource -> ByteString -> Hsc HieFile
218+mkHieFile ms ts _ _ = return (HieFile (ms_mod ms) es)
219+ where
220+ es = nameListFromAvails (mkIfaceExports (tcg_exports ts))
221+
222+ml_hie_file :: GHC.ModLocation -> FilePath
223+ml_hie_file ml = ml_hi_file ml ++ ".hie"
224+
225+data HieFile = HieFile {hie_module :: Module, hie_exports :: [(SrcSpan, Name)]}
226+
227+hieExportNames :: HieFile -> [(SrcSpan, Name)]
228+hieExportNames = hie_exports
229+
230+instance Binary HieFile where
231+ put_ bh (HieFile m es) = do
232+ put_ bh m
233+ put_ bh es
234+
235+ get bh = do
236+ mod <- get bh
237+ es <- get bh
238+ return (HieFile mod es)
239+
240+data HieFileResult = HieFileResult { hie_file_result :: HieFile }
241+
242+writeHieFile :: FilePath -> HieFile -> IO ()
243+readHieFile :: NameCache -> FilePath -> IO (HieFileResult, ())
244+supportsHieFiles :: Bool
245+
246+#if MIN_GHC_API_VERSION(8,6,0)
247+
248+writeHieFile fp hie = do
249+ bh <- openBinMem (1024 * 1024)
250+ putWithUserData (const $ return ()) bh hie
251+ writeBinMem bh fp
252+
253+readHieFile nc fp = do
254+ bh <- readBinMem fp
255+ nc' <- newIORef nc
256+ hie_file <- getWithUserData (NCU (atomicModifyIORef' nc')) bh
257+ return (HieFileResult hie_file, ())
258+
259+supportsHieFiles = True
260+
261+#else
262+
263+supportsHieFiles = False
264+
265+writeHieFile _ _ = return ()
266+
267+readHieFile _ _ = return undefined
268+
269+#endif
270+
271+#endif
272+
273+getHeaderImports
274+ :: DynFlags
275+ -> StringBuffer
276+ -> FilePath
277+ -> FilePath
278+ -> IO
279+ ( Either
280+ ErrorMessages
281+ ( [(Maybe FastString, Located ModuleName)]
282+ , [(Maybe FastString, Located ModuleName)]
283+ , Located ModuleName
284+ )
285+ )
286+#if MIN_GHC_API_VERSION(8,8,0)
287+getHeaderImports = Hdr.getImports
288+#else
289+getHeaderImports a b c d =
290+ catch (Right <$> Hdr.getImports a b c d)
291+ (return . Left . srcErrorMessages)
292+#endif
293+
294+getModuleHash :: ModIface -> Fingerprint
295+#if MIN_GHC_API_VERSION(8,10,0)
296+getModuleHash = mi_mod_hash . mi_final_exts
297+#else
298+getModuleHash = mi_mod_hash
299+#endif
300+
301+getConArgs :: ConDecl pass -> HsConDeclDetails pass
302+#if MIN_GHC_API_VERSION(8,6,0)
303+getConArgs = GHC.getConArgs
304+#else
305+getConArgs = GHC.getConDetails
306+#endif
307+
308+getPackageName :: DynFlags -> Module.InstalledUnitId -> Maybe PackageName
309+getPackageName dfs i = packageName <$> lookupPackage dfs (Module.DefiniteUnitId (Module.DefUnitId i))
src/Development/IDE/GHC/Error.hsnew file+202
@@ -0,0 +1,202 @@
1+-- Copyright (c) 2019 The DAML Authors. All rights reserved.
2+-- SPDX-License-Identifier: Apache-2.0
3+module Development.IDE.GHC.Error
4+ (
5+ -- * Producing Diagnostic values
6+ diagFromErrMsgs
7+ , diagFromErrMsg
8+ , diagFromString
9+ , diagFromStrings
10+ , diagFromGhcException
11+ , catchSrcErrors
12+ , mergeDiagnostics
13+
14+ -- * utilities working with spans
15+ , srcSpanToLocation
16+ , srcSpanToRange
17+ , srcSpanToFilename
18+ , zeroSpan
19+ , realSpan
20+ , isInsideSrcSpan
21+ , noSpan
22+
23+ -- * utilities working with severities
24+ , toDSeverity
25+ ) where
26+
27+import Development.IDE.Types.Diagnostics as D
28+import qualified Data.Text as T
29+import Development.IDE.Types.Location
30+import Development.IDE.GHC.Orphans()
31+import qualified FastString as FS
32+import GHC
33+import Bag
34+import DynFlags
35+import HscTypes
36+import Panic
37+import ErrUtils
38+import SrcLoc
39+import qualified Outputable as Out
40+import Exception (ExceptionMonad)
41+
42+
43+
44+diagFromText :: T.Text -> D.DiagnosticSeverity -> SrcSpan -> T.Text -> FileDiagnostic
45+diagFromText diagSource sev loc msg = (toNormalizedFilePath' $ srcSpanToFilename loc,ShowDiag,)
46+ Diagnostic
47+ { _range = srcSpanToRange loc
48+ , _severity = Just sev
49+ , _source = Just diagSource -- not shown in the IDE, but useful for ghcide developers
50+ , _message = msg
51+ , _code = Nothing
52+ , _relatedInformation = Nothing
53+ , _tags = Nothing
54+ }
55+
56+-- | Produce a GHC-style error from a source span and a message.
57+diagFromErrMsg :: T.Text -> DynFlags -> ErrMsg -> [FileDiagnostic]
58+diagFromErrMsg diagSource dflags e =
59+ [ diagFromText diagSource sev (errMsgSpan e) $ T.pack $ Out.showSDoc dflags $
60+ ErrUtils.formatErrDoc dflags $ ErrUtils.errMsgDoc e
61+ | Just sev <- [toDSeverity $ errMsgSeverity e]]
62+
63+
64+diagFromErrMsgs :: T.Text -> DynFlags -> Bag ErrMsg -> [FileDiagnostic]
65+diagFromErrMsgs diagSource dflags = concatMap (diagFromErrMsg diagSource dflags) . bagToList
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
86+
87+-- | Convert a GHC SrcSpan to a DAML compiler Range
88+srcSpanToRange :: SrcSpan -> Range
89+srcSpanToRange (UnhelpfulSpan _) = noRange
90+srcSpanToRange (RealSrcSpan real) = realSrcSpanToRange real
91+
92+realSrcSpanToRange :: RealSrcSpan -> Range
93+realSrcSpanToRange real =
94+ Range (Position (srcSpanStartLine real - 1) (srcSpanStartCol real - 1))
95+ (Position (srcSpanEndLine real - 1) (srcSpanEndCol real - 1))
96+
97+-- | Extract a file name from a GHC SrcSpan (use message for unhelpful ones)
98+-- FIXME This may not be an _absolute_ file name, needs fixing.
99+srcSpanToFilename :: SrcSpan -> FilePath
100+srcSpanToFilename (UnhelpfulSpan fs) = FS.unpackFS fs
101+srcSpanToFilename (RealSrcSpan real) = FS.unpackFS $ srcSpanFile real
102+
103+srcSpanToLocation :: SrcSpan -> Location
104+srcSpanToLocation src =
105+ -- important that the URI's we produce have been properly normalized, otherwise they point at weird places in VS Code
106+ Location (fromNormalizedUri $ filePathToUri' $ toNormalizedFilePath' $ srcSpanToFilename src) (srcSpanToRange src)
107+
108+isInsideSrcSpan :: Position -> SrcSpan -> Bool
109+p `isInsideSrcSpan` r = sp <= p && p <= ep
110+ where Range sp ep = srcSpanToRange r
111+
112+-- | Convert a GHC severity to a DAML compiler Severity. Severities below
113+-- "Warning" level are dropped (returning Nothing).
114+toDSeverity :: GHC.Severity -> Maybe D.DiagnosticSeverity
115+toDSeverity SevOutput = Nothing
116+toDSeverity SevInteractive = Nothing
117+toDSeverity SevDump = Nothing
118+toDSeverity SevInfo = Just DsInfo
119+toDSeverity SevWarning = Just DsWarning
120+toDSeverity SevError = Just DsError
121+toDSeverity SevFatal = Just DsError
122+
123+
124+-- | Produce a bag of GHC-style errors (@ErrorMessages@) from the given
125+-- (optional) locations and message strings.
126+diagFromStrings :: T.Text -> D.DiagnosticSeverity -> [(SrcSpan, String)] -> [FileDiagnostic]
127+diagFromStrings diagSource sev = concatMap (uncurry (diagFromString diagSource sev))
128+
129+-- | Produce a GHC-style error from a source span and a message.
130+diagFromString :: T.Text -> D.DiagnosticSeverity -> SrcSpan -> String -> [FileDiagnostic]
131+diagFromString diagSource sev sp x = [diagFromText diagSource sev sp $ T.pack x]
132+
133+
134+-- | Produces an "unhelpful" source span with the given string.
135+noSpan :: String -> SrcSpan
136+noSpan = UnhelpfulSpan . FS.fsLit
137+
138+
139+-- | creates a span with zero length in the filename of the argument passed
140+zeroSpan :: FS.FastString -- ^ file path of span
141+ -> RealSrcSpan
142+zeroSpan file = realSrcLocSpan (mkRealSrcLoc file 1 1)
143+
144+realSpan :: SrcSpan
145+ -> Maybe RealSrcSpan
146+realSpan = \case
147+ RealSrcSpan r -> Just r
148+ UnhelpfulSpan _ -> Nothing
149+
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+
164+diagFromGhcException :: T.Text -> DynFlags -> GhcException -> [FileDiagnostic]
165+diagFromGhcException diagSource dflags exc = diagFromString diagSource DsError (noSpan "<Internal>") (showGHCE dflags exc)
166+
167+showGHCE :: DynFlags -> GhcException -> String
168+showGHCE dflags exc = case exc of
169+ Signal n
170+ -> "Signal: " <> show n
171+
172+ Panic s
173+ -> unwords ["Compilation Issue:", s, "\n", requestReport]
174+ PprPanic s sdoc
175+ -> unlines ["Compilation Issue", s,""
176+ , Out.showSDoc dflags sdoc
177+ , requestReport ]
178+
179+ Sorry s
180+ -> "Unsupported feature: " <> s
181+ PprSorry s sdoc
182+ -> unlines ["Unsupported feature: ", s,""
183+ , Out.showSDoc dflags sdoc]
184+
185+
186+ ---------- errors below should not happen at all --------
187+ InstallationError str
188+ -> "Installation error: " <> str
189+
190+ UsageError str -- should never happen
191+ -> unlines ["Unexpected usage error", str]
192+
193+ CmdLineError str
194+ -> unlines ["Unexpected usage error", str]
195+
196+ ProgramError str
197+ -> "Program error: " <> str
198+ PprProgramError str sdoc ->
199+ unlines ["Program error:", str,""
200+ , Out.showSDoc dflags sdoc]
201+ where
202+ requestReport = "Please report this bug to the compiler authors."
src/Development/IDE/GHC/Orphans.hsnew file+72
@@ -0,0 +1,72 @@
1+-- Copyright (c) 2019 The DAML Authors. All rights reserved.
2+-- SPDX-License-Identifier: Apache-2.0
3+
4+{-# LANGUAGE CPP #-}
5+{-# LANGUAGE FlexibleInstances #-}
6+{-# OPTIONS_GHC -Wno-orphans #-}
7+#include "ghc-api-version.h"
8+
9+-- | Orphan instances for GHC.
10+-- Note that the 'NFData' instances may not be law abiding.
11+module Development.IDE.GHC.Orphans() where
12+
13+import GHC
14+import GhcPlugins
15+import Development.IDE.GHC.Compat
16+import qualified StringBuffer as SB
17+import Control.DeepSeq
18+import Data.Hashable
19+import Development.IDE.GHC.Util
20+
21+
22+-- Orphan instances for types from the GHC API.
23+instance Show CoreModule where show = prettyPrint
24+instance NFData CoreModule where rnf = rwhnf
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
32+
33+instance Show InstalledUnitId where
34+ show = installedUnitIdString
35+
36+instance NFData InstalledUnitId where rnf = rwhnf . installedUnitIdFS
37+
38+instance NFData SB.StringBuffer where rnf = rwhnf
39+
40+instance Show Module where
41+ show = moduleNameString . moduleName
42+
43+instance Show (GenLocated SrcSpan ModuleName) where show = prettyPrint
44+
45+instance (NFData l, NFData e) => NFData (GenLocated l e) where
46+ rnf (L l e) = rnf l `seq` rnf e
47+
48+instance Show ModSummary where
49+ show = show . ms_mod
50+
51+instance Show ParsedModule where
52+ show = show . pm_mod_summary
53+
54+instance NFData ModSummary where
55+ rnf = rwhnf
56+
57+#if !MIN_GHC_API_VERSION(8,10,0)
58+instance NFData FastString where
59+ rnf = rwhnf
60+#endif
61+
62+instance NFData ParsedModule where
63+ rnf = rwhnf
64+
65+instance Hashable InstalledUnitId where
66+ hashWithSalt salt = hashWithSalt salt . installedUnitIdString
67+
68+instance Show HieFile where
69+ show = show . hie_module
70+
71+instance NFData HieFile where
72+ rnf = rwhnf
src/Development/IDE/GHC/Util.hsnew file+315
@@ -0,0 +1,315 @@
1+-- Copyright (c) 2019 The DAML Authors. All rights reserved.
2+-- SPDX-License-Identifier: Apache-2.0
3+
4+-- | General utility functions, mostly focused around GHC operations.
5+module Development.IDE.GHC.Util(
6+ -- * HcsEnv and environment
7+ HscEnvEq(GhcVersionMismatch, compileTime, runTime), hscEnv, newHscEnvEq,
8+ modifyDynFlags,
9+ evalGhcEnv,
10+ runGhcEnv,
11+ deps,
12+ -- * GHC wrappers
13+ prettyPrint,
14+ printRdrName,
15+ printName,
16+ ParseResult(..), runParser,
17+ lookupPackageConfig,
18+ textToStringBuffer,
19+ stringBufferToByteString,
20+ moduleImportPath,
21+ cgGutsToCoreModule,
22+ fingerprintToBS,
23+ fingerprintFromStringBuffer,
24+ -- * General utilities
25+ readFileUtf8,
26+ hDuplicateTo',
27+ setDefaultHieDir,
28+ dontWriteHieFiles
29+ ) where
30+
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
37+import Fingerprint
38+import GhcMonad
39+import Control.Exception
40+import Data.IORef
41+import Data.Version (showVersion, Version)
42+import FileCleanup
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
56+import qualified Data.Text.Encoding.Error as T
57+import qualified Data.ByteString as BS
58+import Lexer
59+import 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
74+
75+
76+----------------------------------------------------------------------
77+-- GHC setup
78+
79+-- | Used to modify dyn flags in preference to calling 'setSessionDynFlags',
80+-- since that function also reloads packages (which is very slow).
81+modifyDynFlags :: GhcMonad m => (DynFlags -> DynFlags) -> m ()
82+modifyDynFlags f = do
83+ newFlags <- f <$> getSessionDynFlags
84+ -- We do not use setSessionDynFlags here since we handle package
85+ -- initialization separately.
86+ modifySession $ \h ->
87+ h { hsc_dflags = newFlags, hsc_IC = (hsc_IC h) {ic_dflags = newFlags} }
88+
89+-- | Given a 'UnitId' try and find the associated 'PackageConfig' in the environment.
90+lookupPackageConfig :: UnitId -> HscEnv -> Maybe PackageConfig
91+lookupPackageConfig unitId env =
92+ lookupPackage' False pkgConfigMap unitId
93+ where
94+ pkgConfigMap =
95+ -- For some weird reason, the GHC API does not provide a way to get the PackageConfigMap
96+ -- from PackageState so we have to wrap it in DynFlags first.
97+ getPackageConfigMap $ hsc_dflags env
98+
99+
100+-- | Convert from the @text@ package to the @GHC@ 'StringBuffer'.
101+-- Currently implemented somewhat inefficiently (if it ever comes up in a profile).
102+textToStringBuffer :: T.Text -> StringBuffer
103+textToStringBuffer = stringToStringBuffer . T.unpack
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
112+
113+stringBufferToByteString :: StringBuffer -> ByteString
114+stringBufferToByteString StringBuffer{..} = PS buf cur len
115+
116+-- | Pretty print a GHC value using 'unsafeGlobalDynFlags '.
117+prettyPrint :: Outputable a => a -> String
118+prettyPrint = showSDoc unsafeGlobalDynFlags . ppr
119+
120+-- | Pretty print a 'RdrName' wrapping operators in parens
121+printRdrName :: RdrName -> String
122+printRdrName name = showSDocUnsafe $ parenSymOcc rn (ppr rn)
123+ where
124+ rn = rdrNameOcc name
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
134+
135+-- | Run a 'Ghc' monad value using an existing 'HscEnv'. Sets up and tears down all the required
136+-- pieces, but designed to be more efficient than a standard 'runGhc'.
137+runGhcEnv :: HscEnv -> Ghc a -> IO (HscEnv, a)
138+runGhcEnv env act = do
139+ filesToClean <- newIORef emptyFilesToClean
140+ dirsToClean <- newIORef mempty
141+ let dflags = (hsc_dflags env){filesToClean=filesToClean, dirsToClean=dirsToClean, useUnicode=True}
142+ ref <- newIORef env{hsc_dflags=dflags}
143+ res <- unGhc act (Session ref) `finally` do
144+ cleanTempFiles dflags
145+ cleanTempDirs dflags
146+ (,res) <$> readIORef ref
147+
148+-- | Given a module location, and its parse tree, figure out what is the include directory implied by it.
149+-- For example, given the file @\/usr\/\Test\/Foo\/Bar.hs@ with the module name @Foo.Bar@ the directory
150+-- @\/usr\/Test@ should be on the include path to find sibling modules.
151+moduleImportPath :: NormalizedFilePath -> GHC.ModuleName -> Maybe FilePath
152+-- The call to takeDirectory is required since DAML does not require that
153+-- the file name matches the module name in the last component.
154+-- Once that has changed we can get rid of this.
155+moduleImportPath (takeDirectory . fromNormalizedFilePath -> pathDir) mn
156+ -- This happens for single-component modules since takeDirectory "A" == "."
157+ | modDir == "." = Just pathDir
158+ | otherwise = dropTrailingPathSeparator <$> stripSuffix modDir pathDir
159+ where
160+ -- A for module A.B
161+ modDir =
162+ takeDirectory $
163+ fromNormalizedFilePath $ toNormalizedFilePath' $
164+ moduleNameSlashes mn
165+
166+-- | An 'HscEnv' with equality. Two values are considered equal
167+-- if they are created with the same call to 'newHscEnvEq'.
168+data HscEnvEq
169+ = HscEnvEq !Unique !HscEnv
170+ [(InstalledUnitId, DynFlags)] -- In memory components for this HscEnv
171+ -- This is only used at the moment for the import dirs in
172+ -- the DynFlags
173+ | GhcVersionMismatch { compileTime :: !Version
174+ , runTime :: !Version
175+ }
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/Functions/Warnings.hs → src/Development/IDE/GHC/Warnings.hsrenamed+13−13
@@ -1,20 +1,19 @@
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.
22-- SPDX-License-Identifier: Apache-2.0
33
4−module Development.IDE.Functions.Warnings(withWarnings) where
4+module Development.IDE.GHC.Warnings(withWarnings) where
55
6−import ErrUtils
76import GhcMonad
7+import ErrUtils
88import GhcPlugins as GHC hiding (Var)
99
10−import qualified Data.Text as T
11−import Data.Maybe
1210import Control.Concurrent.Extra
1311import Control.Monad.Extra
12+import qualified Data.Text as T
1413
1514import Development.IDE.Types.Diagnostics
16−import Development.IDE.UtilGHC
17−import Development.IDE.Functions.GHCError
15+import Development.IDE.GHC.Util
16+import Development.IDE.GHC.Error
1817
1918
2019-- | Take a GHC monadic action (e.g. @typecheckModule pm@ for some
@@ -26,18 +25,19 @@ import Development.IDE.Functions.GHCError
2625-- https://github.com/ghc/ghc/blob/5f1d949ab9e09b8d95319633854b7959df06eb58/compiler/main/GHC.hs#L623-L640
2726-- which basically says that log_action is taken from the ModSummary when GHC feels like it.
2827-- The given argument lets you refresh a ModSummary log_action
29−withWarnings :: GhcMonad m => T.Text -> ((ModSummary -> ModSummary) -> m a) -> m ([FileDiagnostic], a)
30−withWarnings phase action = do
28+withWarnings :: GhcMonad m => T.Text -> ((ModSummary -> ModSummary) -> m a) -> m ([(WarnReason, FileDiagnostic)], a)
29+withWarnings diagSource action = do
3130 warnings <- liftIO $ newVar []
3231 oldFlags <- getDynFlags
33− let newAction dynFlags _ _ loc _ msg = do
34− let d = mkDiag dynFlags phase $ mkPlainWarnMsg dynFlags loc msg
35− modifyVar_ warnings $ return . (d:)
32+ let newAction :: DynFlags -> WarnReason -> Severity -> SrcSpan -> PprStyle -> SDoc -> IO ()
33+ newAction dynFlags wr _ loc style msg = do
34+ let wr_d = fmap (wr,) $ diagFromErrMsg diagSource dynFlags $ mkWarnMsg dynFlags loc (queryQual style) msg
35+ modifyVar_ warnings $ return . (wr_d:)
3636 setLogAction newAction
3737 res <- action $ \x -> x{ms_hspp_opts = (ms_hspp_opts x){log_action = newAction}}
3838 setLogAction $ log_action oldFlags
3939 warns <- liftIO $ readVar warnings
40− return (reverse $ catMaybes warns, res)
40+ return (reverse $ concat warns, res)
4141
4242setLogAction :: GhcMonad m => LogAction -> m ()
4343setLogAction act = void $ modifyDynFlags $ \dyn -> dyn{log_action = act}
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.hsnew file+376
@@ -0,0 +1,376 @@
1+-- Copyright (c) 2019 The DAML Authors. All rights reserved.
2+-- SPDX-License-Identifier: Apache-2.0
3+
4+module Development.IDE.Import.DependencyInformation
5+ ( DependencyInformation(..)
6+ , ModuleImports(..)
7+ , RawDependencyInformation(..)
8+ , NodeError(..)
9+ , ModuleParseError(..)
10+ , TransitiveDependencies(..)
11+ , FilePathId(..)
12+ , NamedModuleDep(..)
13+
14+ , PathIdMap
15+ , emptyPathIdMap
16+ , getPathId
17+ , lookupPathToId
18+ , insertImport
19+ , pathToId
20+ , idToPath
21+ , reachableModules
22+ , processDependencyInformation
23+ , transitiveDeps
24+
25+ , BootIdMap
26+ , insertBootId
27+ ) where
28+
29+import Control.DeepSeq
30+import Data.Bifunctor
31+import Data.Coerce
32+import Data.List
33+import Data.Tuple.Extra hiding (first, second)
34+import Development.IDE.GHC.Orphans()
35+import Data.Either
36+import Data.Graph
37+import Data.HashMap.Strict (HashMap)
38+import qualified Data.HashMap.Strict as HMS
39+import Data.List.NonEmpty (NonEmpty(..), nonEmpty)
40+import qualified Data.List.NonEmpty as NonEmpty
41+import Data.IntMap (IntMap)
42+import qualified Data.IntMap.Strict as IntMap
43+import qualified Data.IntMap.Lazy as IntMapLazy
44+import Data.IntSet (IntSet)
45+import qualified Data.IntSet as IntSet
46+import Data.Maybe
47+import Data.Set (Set)
48+import qualified Data.Set as Set
49+import GHC.Generics (Generic)
50+
51+import Development.IDE.Types.Diagnostics
52+import Development.IDE.Types.Location
53+import Development.IDE.Import.FindImports (ArtifactsLocation(..))
54+
55+import GHC
56+import Module
57+
58+-- | The imports for a given module.
59+data ModuleImports = ModuleImports
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)
63+ , packageImports :: !(Set InstalledUnitId)
64+ -- ^ Transitive package dependencies unioned for all imports.
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)
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
136+
137+data DependencyInformation =
138+ DependencyInformation
139+ { depErrorNodes :: !(FilePathIdMap (NonEmpty NodeError))
140+ -- ^ Nodes that cannot be processed correctly.
141+ , depModuleNames :: !(FilePathIdMap ShowableModuleName)
142+ , depModuleDeps :: !(FilePathIdMap FilePathIdSet)
143+ -- ^ For a non-error node, this contains the set of module immediate dependencies
144+ -- in the same package.
145+ , depPkgDeps :: !(FilePathIdMap (Set InstalledUnitId))
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
151+ } deriving (Show, Generic)
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+
163+instance NFData DependencyInformation
164+
165+-- | This does not contain the actual parse error as that is already reported by GetParsedModule.
166+data ModuleParseError = ModuleParseError
167+ deriving (Show, Generic)
168+
169+instance NFData ModuleParseError
170+
171+-- | Error when trying to locate a module.
172+data LocateError = LocateError [Diagnostic]
173+ deriving (Eq, Show, Generic)
174+
175+instance NFData LocateError
176+
177+-- | An error attached to a node in the dependency graph.
178+data NodeError
179+ = PartOfCycle (Located ModuleName) [FilePathId]
180+ -- ^ This module is part of an import cycle. The module name corresponds
181+ -- to the import that enters the cycle starting from this module.
182+ -- The list of filepaths represents the elements
183+ -- in the cycle in unspecified order.
184+ | FailedToLocateImport (Located ModuleName)
185+ -- ^ This module has an import that couldn’t be located.
186+ | ParseError ModuleParseError
187+ | ParentOfErrorNode (Located ModuleName)
188+ -- ^ This module is the parent of a module that cannot be
189+ -- processed (either it cannot be parsed, is part of a cycle
190+ -- or the parent of another error node).
191+ deriving (Show, Generic)
192+
193+instance NFData NodeError where
194+ rnf (PartOfCycle m fs) = m `seq` rnf fs
195+ rnf (FailedToLocateImport m) = m `seq` ()
196+ rnf (ParseError e) = rnf e
197+ rnf (ParentOfErrorNode m) = m `seq` ()
198+
199+-- | A processed node in the dependency graph. If there was any error
200+-- during processing the node or any of its dependencies, this is an
201+-- `ErrorNode`. Otherwise it is a `SuccessNode`.
202+data NodeResult
203+ = ErrorNode (NonEmpty NodeError)
204+ | SuccessNode [(Located ModuleName, FilePathId)]
205+ deriving Show
206+
207+partitionNodeResults
208+ :: [(a, NodeResult)]
209+ -> ([(a, NonEmpty NodeError)], [(a, [(Located ModuleName, FilePathId)])])
210+partitionNodeResults = partitionEithers . map f
211+ where f (a, ErrorNode errs) = Left (a, errs)
212+ f (a, SuccessNode imps) = Right (a, imps)
213+
214+instance Semigroup NodeResult where
215+ ErrorNode errs <> ErrorNode errs' = ErrorNode (errs <> errs')
216+ ErrorNode errs <> SuccessNode _ = ErrorNode errs
217+ SuccessNode _ <> ErrorNode errs = ErrorNode errs
218+ SuccessNode a <> SuccessNode _ = SuccessNode a
219+
220+processDependencyInformation :: RawDependencyInformation -> DependencyInformation
221+processDependencyInformation rawDepInfo@RawDependencyInformation{..} =
222+ DependencyInformation
223+ { depErrorNodes = IntMap.fromList errorNodes
224+ , depModuleDeps = moduleDeps
225+ , depModuleNames = IntMap.fromList $ coerce moduleNames
226+ , depPkgDeps = pkgDependencies rawDepInfo
227+ , depPathIdMap = rawPathIdMap
228+ , depBootMap = rawBootMap
229+ }
230+ where resultGraph = buildResultGraph rawImports
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])]
236+ successEdges =
237+ map
238+ (\(file, imports) -> (FilePathId file, FilePathId file, map snd imports))
239+ successNodes
240+ moduleDeps =
241+ IntMap.fromList $
242+ map (\(_, FilePathId v, vs) -> (v, IntSet.fromList $ coerce vs))
243+ successEdges
244+
245+-- | Given a dependency graph, buildResultGraph detects and propagates errors in that graph as follows:
246+-- 1. Mark each node that is part of an import cycle as an error node.
247+-- 2. Mark each node that has a parse error as an error node.
248+-- 3. Mark each node whose immediate children could not be located as an error.
249+-- 4. Recursively propagate errors to parents if they are not already error nodes.
250+buildResultGraph :: FilePathIdMap (Either ModuleParseError ModuleImports) -> FilePathIdMap NodeResult
251+buildResultGraph g = propagatedErrors
252+ where
253+ sccs = stronglyConnComp (graphEdges g)
254+ (_, cycles) = partitionSCC sccs
255+ cycleErrors :: IntMap NodeResult
256+ cycleErrors = IntMap.unionsWith (<>) $ map errorsForCycle cycles
257+ errorsForCycle :: [FilePathId] -> IntMap NodeResult
258+ errorsForCycle files =
259+ IntMap.fromListWith (<>) $ coerce $ concatMap (cycleErrorsForFile files) files
260+ cycleErrorsForFile :: [FilePathId] -> FilePathId -> [(FilePathId,NodeResult)]
261+ cycleErrorsForFile cycle f =
262+ let entryPoints = mapMaybe (findImport f) cycle
263+ in map (\imp -> (f, ErrorNode (PartOfCycle imp cycle :| []))) entryPoints
264+ otherErrors = IntMap.map otherErrorsForFile g
265+ otherErrorsForFile :: Either ModuleParseError ModuleImports -> NodeResult
266+ otherErrorsForFile (Left err) = ErrorNode (ParseError err :| [])
267+ otherErrorsForFile (Right ModuleImports{moduleImports}) =
268+ let toEither (imp, Nothing) = Left imp
269+ toEither (imp, Just path) = Right (imp, path)
270+ (errs, imports') = partitionEithers (map toEither moduleImports)
271+ in case nonEmpty errs of
272+ Nothing -> SuccessNode imports'
273+ Just errs' -> ErrorNode (NonEmpty.map FailedToLocateImport errs')
274+
275+ unpropagatedErrors = IntMap.unionWith (<>) cycleErrors otherErrors
276+ -- The recursion here is fine since we use a lazy map and
277+ -- we only recurse on SuccessNodes. In particular, we do not recurse
278+ -- on nodes that are part of a cycle as they are already marked as
279+ -- error nodes.
280+ propagatedErrors =
281+ IntMapLazy.map propagate unpropagatedErrors
282+ propagate :: NodeResult -> NodeResult
283+ propagate n@(ErrorNode _) = n
284+ propagate n@(SuccessNode imps) =
285+ let results = map (\(imp, FilePathId dep) -> (imp, propagatedErrors IntMap.! dep)) imps
286+ (errs, _) = partitionNodeResults results
287+ in case nonEmpty errs of
288+ Nothing -> n
289+ Just errs' -> ErrorNode (NonEmpty.map (ParentOfErrorNode . fst) errs')
290+ findImport :: FilePathId -> FilePathId -> Maybe (Located ModuleName)
291+ findImport (FilePathId file) importedFile =
292+ case g IntMap.! file of
293+ Left _ -> error "Tried to call findImport on a module with a parse error"
294+ Right ModuleImports{moduleImports} ->
295+ fmap fst $ find (\(_, resolvedImp) -> resolvedImp == Just importedFile) moduleImports
296+
297+graphEdges :: FilePathIdMap (Either ModuleParseError ModuleImports) -> [(FilePathId, FilePathId, [FilePathId])]
298+graphEdges g =
299+ map (\(k, v) -> (FilePathId k, FilePathId k, deps v)) $ IntMap.toList g
300+ where deps :: Either e ModuleImports -> [FilePathId]
301+ deps (Left _) = []
302+ deps (Right ModuleImports{moduleImports}) = mapMaybe snd moduleImports
303+
304+partitionSCC :: [SCC a] -> ([a], [[a]])
305+partitionSCC (CyclicSCC xs:rest) = second (xs:) $ partitionSCC rest
306+partitionSCC (AcyclicSCC x:rest) = first (x:) $ partitionSCC rest
307+partitionSCC [] = ([], [])
308+
309+
310+transitiveDeps :: DependencyInformation -> NormalizedFilePath -> Maybe TransitiveDependencies
311+transitiveDeps DependencyInformation{..} file = do
312+ let !fileId = pathToId depPathIdMap file
313+ reachableVs <-
314+ -- Delete the starting node
315+ IntSet.delete (getFilePathId fileId) .
316+ IntSet.fromList . map (fst3 . fromVertex) .
317+ reachable g <$> toVertex (getFilePathId fileId)
318+ let transitiveModuleDepIds =
319+ filter (\v -> v `IntSet.member` reachableVs) $ map (fst3 . fromVertex) vs
320+ let transitivePkgDeps =
321+ Set.toList $ Set.unions $
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+ ]
331+ pure TransitiveDependencies {..}
332+ where
333+ (g, fromVertex, toVertex) = graphFromEdges edges
334+ edges = map (\(f, fs) -> (f, f, IntSet.toList fs ++ boot_edge f)) $ IntMap.toList depModuleDeps
335+
336+ -- Need to add an edge between the .hs and .hs-boot file if it exists
337+ -- so the .hs file gets loaded after the .hs-boot file and the right
338+ -- stuff ends up in the HPT. If you don't have this check then GHC will
339+ -- fail to work with ghcide.
340+ boot_edge f = [getFilePathId f' | Just f' <- [IntMap.lookup f depBootMap]]
341+
342+ vs = topSort g
343+
344+data TransitiveDependencies = TransitiveDependencies
345+ { transitiveModuleDeps :: [NormalizedFilePath]
346+ -- ^ Transitive module dependencies in topological order.
347+ -- The module itself is not included.
348+ , transitiveNamedModuleDeps :: [NamedModuleDep]
349+ -- ^ Transitive module dependencies in topological order.
350+ -- The module itself is not included.
351+ , transitivePkgDeps :: [InstalledUnitId]
352+ -- ^ Transitive pkg dependencies in unspecified order.
353+ } deriving (Eq, Show, Generic)
354+
355+instance 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.hsnew file+179
@@ -0,0 +1,179 @@
1+-- Copyright (c) 2019 The DAML Authors. All rights reserved.
2+-- SPDX-License-Identifier: Apache-2.0
3+
4+{-# LANGUAGE CPP #-}
5+#include "ghc-api-version.h"
6+
7+module Development.IDE.Import.FindImports
8+ ( locateModule
9+ , Import(..)
10+ , ArtifactsLocation(..)
11+ , modSummaryToArtifactsLocation
12+ , isBootLocation
13+ , mkImportDirs
14+ ) where
15+
16+import Development.IDE.GHC.Error as ErrUtils
17+import Development.IDE.GHC.Orphans()
18+import Development.IDE.Types.Diagnostics
19+import Development.IDE.Types.Location
20+import Development.IDE.GHC.Compat
21+-- GHC imports
22+import FastString
23+import qualified Module as M
24+import Packages
25+import Outputable (showSDoc, ppr, pprPanic)
26+import Finder
27+import Control.DeepSeq
28+
29+-- standard imports
30+import Control.Monad.Extra
31+import Control.Monad.IO.Class
32+import System.FilePath
33+import DriverPhases
34+import Data.Maybe
35+
36+data Import
37+ = FileImport !ArtifactsLocation
38+ | PackageImport !M.InstalledUnitId
39+ deriving (Show)
40+
41+data ArtifactsLocation = ArtifactsLocation
42+ { artifactFilePath :: !NormalizedFilePath
43+ , artifactModLocation :: !ModLocation
44+ , artifactIsSource :: !Bool -- ^ True if a module is a source input
45+ }
46+ deriving (Show)
47+
48+instance NFData ArtifactsLocation where
49+ rnf ArtifactsLocation{..} = rnf artifactFilePath `seq` rwhnf artifactModLocation `seq` rnf artifactIsSource
50+
51+isBootLocation :: ArtifactsLocation -> Bool
52+isBootLocation = not . artifactIsSource
53+
54+instance NFData Import where
55+ rnf (FileImport x) = rnf x
56+ rnf (PackageImport x) = rnf x
57+
58+modSummaryToArtifactsLocation :: NormalizedFilePath -> ModSummary -> ArtifactsLocation
59+modSummaryToArtifactsLocation nfp ms = ArtifactsLocation nfp (ms_location ms) (isSource (ms_hsc_src ms))
60+ where
61+ isSource HsSrcFile = True
62+ isSource _ = False
63+
64+
65+-- | locate a module in the file system. Where we go from *daml to Haskell
66+locateModuleFile :: MonadIO m
67+ => [[FilePath]]
68+ -> [String]
69+ -> (NormalizedFilePath -> m Bool)
70+ -> Bool
71+ -> ModuleName
72+ -> m (Maybe NormalizedFilePath)
73+locateModuleFile import_dirss exts doesExist isSource modName = do
74+ let candidates import_dirs =
75+ [ toNormalizedFilePath' (prefix </> M.moduleNameSlashes modName <.> maybeBoot ext)
76+ | prefix <- import_dirs , ext <- exts]
77+ findM doesExist (concatMap candidates import_dirss)
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
89+
90+-- | locate a module in either the file system or the package database. Where we go from *daml to
91+-- Haskell
92+locateModule
93+ :: MonadIO m
94+ => DynFlags
95+ -> [(M.InstalledUnitId, DynFlags)] -- Sets import directories to look in
96+ -> [String]
97+ -> (NormalizedFilePath -> m Bool)
98+ -> Located ModuleName
99+ -> Maybe FastString
100+ -> Bool
101+ -> m (Either [FileDiagnostic] Import)
102+locateModule dflags comp_info exts doesExist modName mbPkgName isSource = do
103+ case mbPkgName of
104+ -- "this" means that we should only look in the current package
105+ Just "this" -> do
106+ lookupLocal [importPaths dflags]
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]
111+ | otherwise -> lookupInPackageDB dflags
112+ Nothing -> do
113+ -- first try to find the module as a file. If we can't find it try to find it in the package
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.
118+ mbFile <- locateModuleFile (importPaths dflags : map snd import_paths) exts doesExist isSource $ unLoc modName
119+ case mbFile of
120+ Nothing -> lookupInPackageDB dflags
121+ Just file -> toModLocation file
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+
134+ lookupInPackageDB dfs =
135+ case lookupModuleWithSuggestions dfs (unLoc modName) mbPkgName of
136+ LookupFound _m pkgConfig -> return $ Right $ PackageImport $ unitId pkgConfig
137+ reason -> return $ Left $ notFoundErr dfs modName reason
138+
139+-- | Don't call this on a found module.
140+notFoundErr :: DynFlags -> Located M.ModuleName -> LookupResult -> [FileDiagnostic]
141+notFoundErr dfs modName reason =
142+ mkError' $ ppr' $ cannotFindModule dfs modName0 $ lookupToFindResult reason
143+ where
144+ mkError' = diagFromString "not found" DsError (getLoc modName)
145+ modName0 = unLoc modName
146+ ppr' = showSDoc dfs
147+ -- We convert the lookup result to a find result to reuse GHC's cannotFindMoudle pretty printer.
148+ lookupToFindResult =
149+ \case
150+ LookupFound _m _pkgConfig ->
151+ pprPanic "Impossible: called lookupToFind on found module." (ppr modName0)
152+ LookupMultiple rs -> FoundMultiple rs
153+ LookupHidden pkg_hiddens mod_hiddens ->
154+ notFound
155+ { fr_pkgs_hidden = map (moduleUnitId . fst) pkg_hiddens
156+ , fr_mods_hidden = map (moduleUnitId . fst) mod_hiddens
157+ }
158+#if MIN_GHC_API_VERSION(8,6,0)
159+ LookupUnusable unusable ->
160+ let unusables' = map get_unusable unusable
161+ get_unusable (m, ModUnusable r) = (moduleUnitId m, r)
162+ get_unusable (_, r) =
163+ pprPanic "findLookupResult: unexpected origin" (ppr r)
164+ in notFound {fr_unusables = unusables'}
165+#endif
166+ LookupNotFound suggest ->
167+ notFound {fr_suggestions = suggest}
168+
169+notFound :: FindResult
170+notFound = NotFound
171+ { fr_paths = []
172+ , fr_pkg = Nothing
173+ , fr_pkgs_hidden = []
174+ , fr_mods_hidden = []
175+#if MIN_GHC_API_VERSION(8,6,0)
176+ , fr_unusables = []
177+#endif
178+ , fr_suggestions = []
179+ }
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.hsnew file+236
@@ -0,0 +1,236 @@
1+-- Copyright (c) 2019 The DAML Authors. All rights reserved.
2+-- SPDX-License-Identifier: Apache-2.0
3+
4+{-# LANGUAGE ExistentialQuantification #-}
5+
6+-- WARNING: A copy of DA.Daml.LanguageServer, try to keep them in sync
7+-- This version removes the daml: handling
8+module Development.IDE.LSP.LanguageServer
9+ ( runLanguageServer
10+ ) where
11+
12+import Language.Haskell.LSP.Types
13+import Language.Haskell.LSP.Types.Capabilities
14+import Development.IDE.LSP.Server
15+import qualified Development.IDE.GHC.Util as Ghcide
16+import qualified Language.Haskell.LSP.Control as LSP
17+import qualified Language.Haskell.LSP.Core as LSP
18+import Control.Concurrent.Chan
19+import Control.Concurrent.Extra
20+import Control.Concurrent.Async
21+import Control.Concurrent.STM
22+import Control.Exception.Safe
23+import Data.Default
24+import Data.Maybe
25+import qualified Data.Set as Set
26+import qualified Data.Text as T
27+import GHC.IO.Handle (hDuplicate)
28+import System.IO
29+import Control.Monad.Extra
30+
31+import Development.IDE.Core.IdeConfiguration
32+import Development.IDE.Core.Shake
33+import Development.IDE.LSP.HoverDefinition
34+import Development.IDE.LSP.Notifications
35+import Development.IDE.LSP.Outline
36+import Development.IDE.Types.Logger
37+import Development.IDE.Core.FileStore
38+import Language.Haskell.LSP.Core (LspFuncs(..))
39+import Language.Haskell.LSP.Messages
40+
41+runLanguageServer
42+ :: forall config. (Show config)
43+ => LSP.Options
44+ -> PartialHandlers config
45+ -> (InitializeRequest -> Either T.Text config)
46+ -> (DidChangeConfigurationNotification -> Either T.Text config)
47+ -> (IO LspId -> (FromServerMessage -> IO ()) -> VFSHandle -> ClientCapabilities -> IO IdeState)
48+ -> IO ()
49+runLanguageServer options userHandlers onInitialConfig onConfigChange getIdeState = do
50+ -- Move stdout to another file descriptor and duplicate stderr
51+ -- to stdout. This guards against stray prints from corrupting the JSON-RPC
52+ -- message stream.
53+ newStdout <- hDuplicate stdout
54+ stderr `Ghcide.hDuplicateTo'` stdout
55+ hSetBuffering stderr NoBuffering
56+ hSetBuffering stdout NoBuffering
57+
58+ -- Print out a single space to assert that the above redirection works.
59+ -- This is interleaved with the logger, hence we just print a space here in
60+ -- order not to mess up the output too much. Verified that this breaks
61+ -- the language server tests without the redirection.
62+ putStr " " >> hFlush stdout
63+
64+ -- Send everything over a channel, since you need to wait until after initialise before
65+ -- LspFuncs is available
66+ clientMsgChan :: Chan (Message config) <- newChan
67+
68+ -- These barriers are signaled when the threads reading from these chans exit.
69+ -- This should not happen but if it does, we will make sure that the whole server
70+ -- dies and can be restarted instead of losing threads silently.
71+ clientMsgBarrier <- newBarrier
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)
80+ writeChan clientMsgChan $ Response r wrap f
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
101+ let PartialHandlers parts =
102+ initializeRequestHandler <>
103+ setHandlersIgnore <> -- least important
104+ setHandlersDefinition <> setHandlersHover <>
105+ setHandlersOutline <>
106+ userHandlers <>
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.
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+ }
118+
119+ void $ waitAnyCancel =<< traverse async
120+ [ void $ LSP.runWithHandles
121+ stdin
122+ newStdout
123+ initializeCallbacks
124+ handlers
125+ (modifyOptions options)
126+ Nothing
127+ , void $ waitBarrier clientMsgBarrier
128+ ]
129+ where
130+ handleInit :: IO () -> (LspId -> IO ()) -> (LspId -> IO ()) -> Chan (Message config) -> LSP.LspFuncs config -> IO (Maybe err)
131+ handleInit exitClientMsg clearReqId waitForCancel clientMsgChan lspFuncs@LSP.LspFuncs{..} = do
132+
133+ ide <- getIdeState getNextReqId sendFunc (makeLSPVFSHandle lspFuncs) clientCapabilities
134+
135+ _ <- flip forkFinally (const exitClientMsg) $ forever $ do
136+ msg <- readChan clientMsgChan
137+ case msg of
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)
149+ ResponseAndRequest x@RequestMessage{_id, _params} wrap wrapNewReq act ->
150+ checkCancelled ide clearReqId waitForCancel lspFuncs wrap act x _id _params $
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
164+ pure Nothing
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)
200+
201+-- | Things that get sent to us, but we don't deal with.
202+-- Set them to avoid a warning in VS Code output.
203+setHandlersIgnore :: PartialHandlers config
204+setHandlersIgnore = PartialHandlers $ \_ x -> return x
205+ {LSP.initializedHandler = none
206+ ,LSP.responseHandler = none
207+ }
208+ where none = Just $ const $ return ()
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+
217+
218+-- | A message that we need to deal with - the pieces are split up with existentials to gain additional type safety
219+-- and defer precise processing until later (allows us to keep at a higher level of abstraction slightly longer)
220+data Message c
221+ = forall m req resp . (Show m, Show req) => Response (RequestMessage m req resp) (ResponseMessage resp -> FromServerMessage) (LSP.LspFuncs c -> IdeState -> req -> IO (Either ResponseError resp))
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)))
226+ | forall m req . (Show m, Show req) => Notification (NotificationMessage m req) (LSP.LspFuncs c -> IdeState -> req -> IO ())
227+ -- | Used for the InitializeRequest only, where the response is generated by the LSP core handler.
228+ | InitialParams InitializeRequest (LSP.LspFuncs c -> IdeState -> InitializeParams -> IO ())
229+
230+modifyOptions :: LSP.Options -> LSP.Options
231+modifyOptions x = x{ LSP.textDocumentSync = Just $ tweakTDS origTDS
232+ }
233+ where
234+ tweakTDS tds = tds{_openClose=Just True, _change=Just TdSyncIncremental, _save=Just $ SaveOptions Nothing}
235+ origTDS = fromMaybe tdsDefault $ LSP.textDocumentSync x
236+ tdsDefault = TextDocumentSyncOptions Nothing Nothing Nothing Nothing Nothing
src/Development/IDE/LSP/Notifications.hsnew file+81
@@ -0,0 +1,81 @@
1+-- Copyright (c) 2019 The DAML Authors. All rights reserved.
2+-- SPDX-License-Identifier: Apache-2.0
3+
4+{-# LANGUAGE DuplicateRecordFields #-}
5+{-# LANGUAGE RankNTypes #-}
6+
7+module Development.IDE.LSP.Notifications
8+ ( setHandlersNotifications
9+ ) where
10+
11+import Development.IDE.LSP.Server
12+import qualified Language.Haskell.LSP.Core as LSP
13+import Language.Haskell.LSP.Types
14+import qualified Language.Haskell.LSP.Types as LSP
15+
16+import Development.IDE.Core.IdeConfiguration
17+import Development.IDE.Core.Service
18+import Development.IDE.Types.Location
19+import Development.IDE.Types.Logger
20+
21+import Control.Monad.Extra
22+import Data.Foldable as F
23+import Data.Maybe
24+import qualified Data.HashSet as S
25+import qualified Data.Text as Text
26+
27+import Development.IDE.Core.FileStore (setSomethingModified)
28+import Development.IDE.Core.FileExists (modifyFileExists)
29+import Development.IDE.Core.OfInterest
30+
31+
32+whenUriFile :: Uri -> (NormalizedFilePath -> IO ()) -> IO ()
33+whenUriFile uri act = whenJust (LSP.uriToFilePath uri) $ act . toNormalizedFilePath'
34+
35+setHandlersNotifications :: PartialHandlers c
36+setHandlersNotifications = PartialHandlers $ \WithMessage{..} x -> return x
37+ {LSP.didOpenTextDocumentNotificationHandler = withNotification (LSP.didOpenTextDocumentNotificationHandler x) $
38+ \_ ide (DidOpenTextDocumentParams TextDocumentItem{_uri,_version}) -> do
39+ updatePositionMapping ide (VersionedTextDocumentIdentifier _uri (Just _version)) (List [])
40+ whenUriFile _uri $ \file -> do
41+ modifyFilesOfInterest ide (S.insert file)
42+ logInfo (ideLogger ide) $ "Opened text document: " <> getUri _uri
43+
44+ ,LSP.didChangeTextDocumentNotificationHandler = withNotification (LSP.didChangeTextDocumentNotificationHandler x) $
45+ \_ ide (DidChangeTextDocumentParams identifier@VersionedTextDocumentIdentifier{_uri} changes) -> do
46+ updatePositionMapping ide identifier changes
47+ setSomethingModified ide
48+ logInfo (ideLogger ide) $ "Modified text document: " <> getUri _uri
49+
50+ ,LSP.didSaveTextDocumentNotificationHandler = withNotification (LSP.didSaveTextDocumentNotificationHandler x) $
51+ \_ ide (DidSaveTextDocumentParams TextDocumentIdentifier{_uri}) -> do
52+ setSomethingModified ide
53+ logInfo (ideLogger ide) $ "Saved text document: " <> getUri _uri
54+
55+ ,LSP.didCloseTextDocumentNotificationHandler = withNotification (LSP.didCloseTextDocumentNotificationHandler x) $
56+ \_ ide (DidCloseTextDocumentParams TextDocumentIdentifier{_uri}) -> do
57+ whenUriFile _uri $ \file -> do
58+ modifyFilesOfInterest ide (S.delete file)
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))
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.hsnew file+23
@@ -0,0 +1,23 @@
1+-- Copyright (c) 2019 The DAML Authors. All rights reserved.
2+-- SPDX-License-Identifier: Apache-2.0
3+{-# LANGUAGE PatternSynonyms #-}
4+
5+module Development.IDE.LSP.Protocol
6+ ( pattern EventFileDiagnostics
7+ ) where
8+
9+import Development.IDE.Types.Diagnostics
10+import Development.IDE.Types.Location
11+import Language.Haskell.LSP.Messages
12+import Language.Haskell.LSP.Types
13+
14+----------------------------------------------------------------------------------------------------
15+-- Pretty printing
16+----------------------------------------------------------------------------------------------------
17+
18+-- | Pattern synonym to make it a bit more convenient to match on diagnostics
19+-- in things like damlc test.
20+pattern EventFileDiagnostics :: FilePath -> [Diagnostic] -> FromServerMessage
21+pattern EventFileDiagnostics fp diags <-
22+ NotPublishDiagnostics
23+ (NotificationMessage _ _ (PublishDiagnosticsParams (uriToFilePath' -> Just fp) (List diags)))
src/Development/IDE/LSP/Server.hsnew file+47
@@ -0,0 +1,47 @@
1+-- Copyright (c) 2019 The DAML Authors. All rights reserved.
2+-- SPDX-License-Identifier: Apache-2.0
3+
4+{-# LANGUAGE DuplicateRecordFields #-}
5+{-# LANGUAGE RankNTypes #-}
6+module Development.IDE.LSP.Server
7+ ( WithMessage(..)
8+ , PartialHandlers(..)
9+ ) where
10+
11+
12+import Data.Default
13+
14+import Language.Haskell.LSP.Types
15+import qualified Language.Haskell.LSP.Core as LSP
16+import qualified Language.Haskell.LSP.Messages as LSP
17+import Development.IDE.Core.Service
18+
19+data WithMessage c = WithMessage
20+ {withResponse :: forall m req resp . (Show m, Show req) =>
21+ (ResponseMessage resp -> LSP.FromServerMessage) -> -- how to wrap a response
22+ (LSP.LspFuncs c -> IdeState -> req -> IO (Either ResponseError resp)) -> -- actual work
23+ Maybe (LSP.Handler (RequestMessage m req resp))
24+ ,withNotification :: forall m req . (Show m, Show req) =>
25+ Maybe (LSP.Handler (NotificationMessage m req)) -> -- old notification handler
26+ (LSP.LspFuncs c -> IdeState -> req -> IO ()) -> -- actual work
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)
36+ }
37+
38+newtype PartialHandlers c = PartialHandlers (WithMessage c -> LSP.Handlers -> IO LSP.Handlers)
39+
40+instance Default (PartialHandlers c) where
41+ def = PartialHandlers $ \_ x -> pure x
42+
43+instance Semigroup (PartialHandlers c) where
44+ PartialHandlers a <> PartialHandlers b = PartialHandlers $ \w x -> a w x >>= b w
45+
46+instance Monoid (PartialHandlers c) where
47+ mempty = def
src/Development/IDE/Logger.hsdeleted−26
@@ -1,26 +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 RankNTypes #-}
5−-- | This is a compatibility module that abstracts over the
6−-- concrete choice of logging framework so users can plug in whatever
7−-- framework they want to.
8−module Development.IDE.Logger
9− ( Handle(..)
10− , makeOneHandle
11− , makeNopHandle
12− ) where
13−
14−import qualified Data.Text as T
15−import GHC.Stack
16−
17−data Handle = Handle {
18− logSeriousError :: HasCallStack => T.Text -> IO ()
19− , logDebug :: HasCallStack => T.Text -> IO ()
20− }
21−
22−makeNopHandle :: Handle
23−makeNopHandle = makeOneHandle $ const $ pure ()
24−
25−makeOneHandle :: (HasCallStack => T.Text -> IO ()) -> Handle
26−makeOneHandle x = Handle x x
src/Development/IDE/Orphans.hsdeleted−40
@@ -1,40 +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 FlexibleInstances #-}
5−{-# OPTIONS_GHC -Wno-orphans #-}
6−
7−-- | Orphan instances for GHC.
8−-- Note that the 'NFData' instances may not be law abiding.
9−module Development.IDE.Orphans() where
10−
11−import GHC hiding (convertLit)
12−import GhcPlugins as GHC hiding (fst3, (<>))
13−import qualified StringBuffer as SB
14−import Control.DeepSeq
15−import Development.IDE.UtilGHC
16−
17−
18−-- Orphan instances for types from the GHC API.
19−instance Show CoreModule where show = prettyPrint
20−instance NFData CoreModule where rnf = rwhnf
21−
22−
23−instance Show InstalledUnitId where
24− show = installedUnitIdString
25−
26−instance NFData InstalledUnitId where rnf = rwhnf
27−
28−instance NFData SB.StringBuffer where rnf = rwhnf
29−
30−instance Show Module where
31− show = moduleNameString . moduleName
32−
33−instance Show RdrName where show = prettyPrint
34−instance Show ComponentId where show = prettyPrint
35−instance Show SourcePackageId where show = prettyPrint
36−instance Show ModuleName where show = prettyPrint
37−instance Show (GenLocated SrcSpan ModuleName) where show = prettyPrint
38−instance Show PackageName where show = prettyPrint
39−instance Show PackageState where show _ = "PackageState"
40−instance Show Name where show = prettyPrint
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.hsnew file+178
@@ -0,0 +1,178 @@
1+-- Copyright (c) 2019 The DAML Authors. All rights reserved.
2+-- SPDX-License-Identifier: Apache-2.0
3+
4+-- | Gives information about symbols at a given point in DAML files.
5+-- These are all pure functions that should execute quickly.
6+module Development.IDE.Spans.AtPoint (
7+ atPoint
8+ , gotoDefinition
9+ ) where
10+
11+import Development.IDE.GHC.Error
12+import Development.IDE.GHC.Orphans()
13+import Development.IDE.Types.Location
14+
15+-- DAML compiler and infrastructure
16+import Development.IDE.GHC.Compat
17+import Development.IDE.Types.Options
18+import Development.IDE.Spans.Type as SpanInfo
19+import Development.IDE.Spans.Common (spanDocToMarkdown)
20+
21+-- GHC API imports
22+import DynFlags
23+import FastString
24+import Name
25+import Outputable hiding ((<>))
26+import SrcLoc
27+import Type
28+import VarSet
29+
30+import Control.Monad.Extra
31+import Control.Monad.Trans.Maybe
32+import Control.Monad.IO.Class
33+import Data.Maybe
34+import Data.List
35+import qualified Data.Text as T
36+
37+-- | Locate the definition of the name at a given position.
38+gotoDefinition
39+ :: MonadIO m
40+ => (Module -> m (Maybe (HieFile, FilePath)))
41+ -> IdeOptions
42+ -> [SpanInfo]
43+ -> Position
44+ -> m (Maybe Location)
45+gotoDefinition getHieFile ideOpts srcSpans pos =
46+ listToMaybe <$> locationsAtPoint getHieFile ideOpts pos srcSpans
47+
48+-- | Synopsis for the name at a given position.
49+atPoint
50+ :: IdeOptions
51+ -> SpansInfo
52+ -> Position
53+ -> Maybe (Maybe Range, [T.Text])
54+atPoint IdeOptions{..} (SpansInfo srcSpans cntsSpans) pos = do
55+ firstSpan <- listToMaybe $ deEmpasizeGeneratedEqShow $ spansAtPoint pos srcSpans
56+ let constraintsAtPoint = mapMaybe spaninfoType (spansAtPoint pos cntsSpans)
57+ return (Just (range firstSpan), hoverInfo firstSpan constraintsAtPoint)
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+
104+ -- NOTE(RJR): This is a bit hacky.
105+ -- We don't want to show the user type signatures generated from Eq and Show
106+ -- instances, as they do not appear in the source program.
107+ -- However the user could have written an `==` or `show` function directly,
108+ -- in which case we still want to show information for that.
109+ -- Hence we just move such information later in the list of spans.
110+ deEmpasizeGeneratedEqShow :: [SpanInfo] -> [SpanInfo]
111+ deEmpasizeGeneratedEqShow = uncurry (++) . partition (not . isTypeclassDeclSpan)
112+ isTypeclassDeclSpan :: SpanInfo -> Bool
113+ isTypeclassDeclSpan spanInfo =
114+ case getNameM (spaninfoSource spanInfo) of
115+ Just name -> any (`isInfixOf` getOccString name) ["==", "showsPrec"]
116+ Nothing -> False
117+
118+locationsAtPoint
119+ :: forall m
120+ . MonadIO m
121+ => (Module -> m (Maybe (HieFile, FilePath)))
122+ -> IdeOptions
123+ -> Position
124+ -> [SpanInfo]
125+ -> m [Location]
126+locationsAtPoint getHieFile _ideOptions pos =
127+ fmap (map srcSpanToLocation) . mapMaybeM (getSpan . spaninfoSource) . spansAtPoint pos
128+ where getSpan :: SpanSource -> m (Maybe SrcSpan)
129+ getSpan NoSource = pure Nothing
130+ getSpan (SpanS sp) = pure $ Just sp
131+ getSpan (Lit _) = pure Nothing
132+ getSpan (Named name) = case nameSrcSpan name of
133+ sp@(RealSrcSpan _) -> pure $ Just sp
134+ sp@(UnhelpfulSpan _) -> runMaybeT $ do
135+ guard (sp /= wiredInSrcSpan)
136+ -- This case usually arises when the definition is in an external package (DAML only).
137+ -- In this case the interface files contain garbage source spans
138+ -- so we instead read the .hie files to get useful source spans.
139+ mod <- MaybeT $ return $ nameModule_maybe name
140+ (hieFile, srcPath) <- MaybeT $ getHieFile mod
141+ avail <- MaybeT $ pure $ find (eqName name . snd) $ hieExportNames hieFile
142+ -- The location will point to the source file used during compilation.
143+ -- This file might no longer exists and even if it does the path will be relative
144+ -- to the compilation directory which we don’t know.
145+ let span = setFileName srcPath $ fst avail
146+ pure span
147+ -- We ignore uniques and source spans and only compare the name and the module.
148+ eqName :: Name -> Name -> Bool
149+ eqName n n' = nameOccName n == nameOccName n' && nameModule_maybe n == nameModule_maybe n'
150+ setFileName f (RealSrcSpan span) = RealSrcSpan (span { srcSpanFile = mkFastString f })
151+ setFileName _ span@(UnhelpfulSpan _) = span
152+
153+-- | Filter out spans which do not enclose a given point
154+spansAtPoint :: Position -> [SpanInfo] -> [SpanInfo]
155+spansAtPoint pos = filter atp where
156+ line = _line pos
157+ cha = _character pos
158+ atp SpanInfo{..} =
159+ startsBeforePosition && endsAfterPosition
160+ where
161+ startLineCmp = compare spaninfoStartLine line
162+ endLineCmp = compare spaninfoEndLine line
163+
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 >=
167+ endsAfterPosition = endLineCmp == GT || (endLineCmp == EQ && spaninfoEndCol > cha)
168+
169+showName :: Outputable a => a -> T.Text
170+showName = T.pack . prettyprint
171+ where
172+ prettyprint x = renderWithStyle unsafeGlobalDynFlags (ppr x) style
173+ style = mkUserStyle unsafeGlobalDynFlags neverQualify AllTheWay
174+
175+getModuleNameAsText :: Name -> Maybe T.Text
176+getModuleNameAsText n = do
177+ m <- nameModule_maybe n
178+ return . T.pack . moduleNameString $ moduleName m
src/Development/IDE/Spans/Calculate.hsnew file+273
@@ -0,0 +1,273 @@
1+-- Copyright (c) 2019 The DAML Authors. All rights reserved.
2+-- SPDX-License-Identifier: Apache-2.0
3+
4+-- ORIGINALLY COPIED FROM https://github.com/commercialhaskell/intero
5+
6+{-# LANGUAGE CPP #-}
7+{-# LANGUAGE RankNTypes #-}
8+#include "ghc-api-version.h"
9+
10+-- | Get information on modules, identifiers, etc.
11+
12+module Development.IDE.Spans.Calculate(getSrcSpanInfos) where
13+
14+import ConLike
15+import Control.Monad
16+import qualified CoreUtils
17+import Data.List
18+import Data.Maybe
19+import DataCon
20+import Desugar
21+import GHC
22+import GhcMonad
23+import HscTypes
24+import FastString (mkFastString)
25+import OccName
26+import Development.IDE.Types.Location
27+import Development.IDE.Spans.Type
28+#ifdef GHC_LIB
29+import Development.IDE.GHC.Error (zeroSpan)
30+#else
31+import Development.IDE.GHC.Error (zeroSpan, catchSrcErrors)
32+#endif
33+import Prelude hiding (mod)
34+import TcHsSyn
35+import 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
61+
62+-- | Get ALL source spans in the module.
63+getSpanInfo :: GhcMonad m
64+ => [(Located ModuleName, Maybe NormalizedFilePath)] -- ^ imports
65+ -> TypecheckedModule
66+ -> [ParsedModule]
67+ -> [ModIface]
68+ -> m SpansInfo
69+getSpanInfo mods tcm@TypecheckedModule{..} parsedDeps deps =
70+ do let tcs = tm_typechecked_source
71+ bs = listifyAllSpans tcs :: [LHsBind GhcTc]
72+ es = listifyAllSpans tcs :: [LHsExpr GhcTc]
73+ ps = listifyAllSpans' tcs :: [Pat GhcTc]
74+ ts = listifyAllSpans tm_renamed_source :: [LHsType GhcRn]
75+ allModules = tm_parsed_module : parsedDeps
76+ funBinds = funBindMap tm_parsed_module
77+
78+ -- Load all modules in HPT to make their interface documentation available
79+ mapM_ (`loadDepModule` Nothing) (reverse deps)
80+ forM_ (modInfoIface tm_checked_module_info) $ \modIface ->
81+ modifySession (loadModuleHome $ HomeModInfo modIface (snd tm_internals_) Nothing)
82+
83+ bts <- mapM (getTypeLHsBind allModules funBinds) bs -- binds
84+ ets <- mapM (getTypeLHsExpr allModules) es -- expressions
85+ pts <- mapM (getTypeLPat allModules) ps -- patterns
86+ tts <- mapM (getLHsType allModules) ts -- types
87+ let imports = importInfo mods
88+ let exports = getExports tcm
89+ let exprs = addEmptyInfo exports ++ addEmptyInfo imports ++ concat bts ++ concat tts ++ catMaybes (ets ++ pts)
90+ let constraints = map constraintToInfo (concatMap getConstraintsLHsBind bs)
91+ return $ SpansInfo (mapMaybe toSpanInfo (sortBy cmp exprs))
92+ (mapMaybe toSpanInfo (sortBy cmp constraints))
93+ where cmp (_,a,_,_) (_,b,_,_)
94+ | a `isSubspanOf` b = LT
95+ | b `isSubspanOf` a = GT
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 ]
109+
110+getExports :: TypecheckedModule -> [(SpanSource, SrcSpan)]
111+getExports m
112+ | Just (_, _, Just exports, _) <- renamedSource m =
113+ [ (Named $ unLoc n, getLoc n)
114+ | (e, _) <- exports
115+ , n <- ieLNames $ unLoc e
116+ ]
117+getExports _ = []
118+
119+-- | Variant of GHC's ieNames that produces LIdP instead of IdP
120+ieLNames :: IE pass -> [Located (IdP pass)]
121+ieLNames (IEVar U n ) = [ieLWrappedName n]
122+ieLNames (IEThingAbs U n ) = [ieLWrappedName n]
123+ieLNames (IEThingAll U n ) = [ieLWrappedName n]
124+ieLNames (IEThingWith U n _ ns _) = ieLWrappedName n : map ieLWrappedName ns
125+ieLNames _ = []
126+
127+-- | Get the name and type of a binding.
128+getTypeLHsBind :: (GhcMonad m)
129+ => [ParsedModule]
130+ -> OccEnv (HsBind GhcPs)
131+ -> LHsBind GhcTc
132+ -> m [(SpanSource, SrcSpan, Maybe Type, SpanDoc)]
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
142+ return [(Named name, getLoc pid, Just (varType (unLoc pid)), docs)]
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 _ = []
151+
152+-- | Get the name and type of an expression.
153+getTypeLHsExpr :: (GhcMonad m)
154+ => [ParsedModule]
155+ -> LHsExpr GhcTc
156+ -> m (Maybe (SpanSource, SrcSpan, Maybe Type, SpanDoc))
157+getTypeLHsExpr deps e = do
158+ hs_env <- getSession
159+ (_, mbe) <- liftIO (deSugarExpr hs_env e)
160+ case mbe of
161+ Just expr -> do
162+ let ss = getSpanSource (unLoc e)
163+ docs <- case ss of
164+ Named n -> getDocumentationTryGhc deps n
165+ _ -> return emptySpanDoc
166+ return $ Just (ss, getLoc e, Just (CoreUtils.exprType expr), docs)
167+ Nothing -> return Nothing
168+ where
169+ getSpanSource :: HsExpr GhcTc -> SpanSource
170+ getSpanSource xpr | isLit xpr = Lit (showGhc xpr)
171+ getSpanSource (HsVar U (L _ i)) = Named (getName i)
172+ getSpanSource (HsConLikeOut U (RealDataCon dc)) = Named (dataConName dc)
173+ getSpanSource RecordCon {rcon_con_name} = Named (getName rcon_con_name)
174+ getSpanSource (HsWrap U _ xpr) = getSpanSource xpr
175+ getSpanSource (HsPar U xpr) = getSpanSource (unLoc xpr)
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
207+
208+-- | Get the name and type of a pattern.
209+getTypeLPat :: (GhcMonad m)
210+ => [ParsedModule]
211+ -> Pat GhcTc
212+ -> m (Maybe (SpanSource, SrcSpan, Maybe Type, SpanDoc))
213+getTypeLPat deps pat = do
214+ let (src, spn) = getSpanSource pat
215+ docs <- case src of
216+ Named n -> getDocumentationTryGhc deps n
217+ _ -> return emptySpanDoc
218+ return $ Just (src, spn, Just (hsPatType pat), docs)
219+ where
220+ getSpanSource :: Pat GhcTc -> (SpanSource, SrcSpan)
221+ getSpanSource (VarPat U (L spn vid)) = (Named (getName vid), spn)
222+ getSpanSource (ConPatOut (L spn (RealDataCon dc)) _ _ _ _ _ _) =
223+ (Named (dataConName dc), spn)
224+ getSpanSource _ = (NoSource, noSrcSpan)
225+
226+getLHsType
227+ :: GhcMonad m
228+ => [ParsedModule]
229+ -> LHsType GhcRn
230+ -> m [(SpanSource, SrcSpan, Maybe Type, SpanDoc)]
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)]
245+getLHsType _ _ = pure []
246+
247+importInfo :: [(Located ModuleName, Maybe NormalizedFilePath)]
248+ -> [(SpanSource, SrcSpan)]
249+importInfo = mapMaybe (uncurry wrk) where
250+ wrk :: Located ModuleName -> Maybe NormalizedFilePath -> Maybe (SpanSource, SrcSpan)
251+ wrk modName = \case
252+ Nothing -> Nothing
253+ Just fp -> Just (fpToSpanSource $ fromNormalizedFilePath fp, getLoc modName)
254+
255+ -- TODO make this point to the module name
256+ fpToSpanSource :: FilePath -> SpanSource
257+ fpToSpanSource fp = SpanS $ RealSrcSpan $ zeroSpan $ mkFastString fp
258+
259+-- | Pretty print the types into a 'SpanInfo'.
260+toSpanInfo :: (SpanSource, SrcSpan, Maybe Type, SpanDoc) -> Maybe SpanInfo
261+toSpanInfo (name,mspan,typ,docs) =
262+ case mspan of
263+ RealSrcSpan spn ->
264+ -- GHC’s line and column numbers are 1-based while LSP’s line and column
265+ -- numbers are 0-based.
266+ Just (SpanInfo (srcSpanStartLine spn - 1)
267+ (srcSpanStartCol spn - 1)
268+ (srcSpanEndLine spn - 1)
269+ (srcSpanEndCol spn - 1)
270+ typ
271+ name
272+ docs)
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/Functions/Documentation.hs → src/Development/IDE/Spans/Documentation.hsrenamed+51−23
@@ -1,9 +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.
22-- SPDX-License-Identifier: Apache-2.0
33
4−module Development.IDE.Functions.Documentation (
4+{-# LANGUAGE CPP #-}
5+#include "ghc-api-version.h"
6+
7+module Development.IDE.Spans.Documentation (
58 getDocumentation
6− , docHeaders
9+ , getDocumentationTryGhc
710 ) where
811
912import Control.Monad
@@ -11,17 +14,35 @@ import Data.List.Extra
1114import qualified Data.Map as M
1215import Data.Maybe
1316import qualified Data.Text as T
14−import Development.IDE.Functions.GHCError
15−import Development.IDE.Functions.SpanInfo
17+import Development.IDE.GHC.Compat
18+import Development.IDE.GHC.Error
19+import Development.IDE.Spans.Common
1620import FastString
17−import GHC
1821import SrcLoc
1922
2023
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+
2142getDocumentation
22− :: Name -- ^ The name you want documentation for.
23− -> [TypecheckedModule] -- ^ All of the possible modules it could be defined in.
24− -> [RealLocated AnnotationComment]
43+ :: [ParsedModule] -- ^ All of the possible modules it could be defined in.
44+ -> Name -- ^ The name you want documentation for.
45+ -> [T.Text]
2546-- This finds any documentation between the name you want
2647-- documentation for and the one before it. This is only an
2748-- approximately correct algorithm and there are easily constructed
@@ -29,17 +50,18 @@ getDocumentation
2950-- may be edge cases where it is very wrong).
3051-- TODO : Build a version of GHC exactprint to extract this information
3152-- more accurately.
32−getDocumentation targetName tcs = fromMaybe [] $ do
53+getDocumentation sources targetName = fromMaybe [] $ do
3354 -- Find the module the target is defined in.
3455 targetNameSpan <- realSpan $ nameSrcSpan targetName
3556 tc <-
36− listToMaybe
37− $ filter ((==) (Just $ srcSpanFile targetNameSpan) . annotationFileName)
38− $ reverse tcs -- TODO : Is reversing the list here really neccessary?
39− -- Names bound by the module (we want to exclude non-"top-level"
40− -- bindings but unfortunately we get all here).
41− let bs = mapMaybe name_of_bind
42− (listifyAllSpans (tm_typechecked_source tc) :: [LHsBind GhcTc])
57+ find ((==) (Just $ srcSpanFile targetNameSpan) . annotationFileName)
58+ $ reverse sources -- TODO : Is reversing the list here really neccessary?
59+
60+ -- Top level names bound by the module
61+ let bs = [ n | let L _ HsModule{hsmodDecls} = pm_parsed_source tc
62+ , L _ (ValD hsbind) <- hsmodDecls
63+ , Just n <- [name_of_bind hsbind]
64+ ]
4365 -- Sort the names' source spans.
4466 let sortedSpans = sortedNameSpans bs
4567 -- Now go ahead and extract the docs.
@@ -52,6 +74,7 @@ getDocumentation targetName tcs = fromMaybe [] $ do
5274 -- Annoyingly "-- |" documentation isn't annotated with a location,
5375 -- so you have to pull it out from the elements.
5476 pure
77+ $ docHeaders
5578 $ filter (\(L target _) -> isBetween target prevNameSpan targetNameSpan)
5679 $ mapMaybe (\(L l v) -> L <$> realSpan l <*> pure v)
5780 $ join
@@ -60,16 +83,16 @@ getDocumentation targetName tcs = fromMaybe [] $ do
6083 where
6184 -- Get the name bound by a binding. We only concern ourselves with
6285 -- @FunBind@ (which covers functions and variables).
63− name_of_bind :: LHsBind GhcTc -> Maybe Name
64− name_of_bind (L _ FunBind {fun_id}) = Just (getName (unLoc fun_id))
86+ name_of_bind :: HsBind GhcPs -> Maybe (Located RdrName)
87+ name_of_bind FunBind {fun_id} = Just fun_id
6588 name_of_bind _ = Nothing
6689 -- Get source spans from names, discard unhelpful spans, remove
6790 -- duplicates and sort.
68− sortedNameSpans :: [Name] -> [RealSrcSpan]
69− sortedNameSpans ls = nubSort (mapMaybe (realSpan . nameSrcSpan) ls)
91+ sortedNameSpans :: [Located RdrName] -> [RealSrcSpan]
92+ sortedNameSpans ls = nubSort (mapMaybe (realSpan . getLoc) ls)
7093 isBetween target before after = before <= target && target <= after
71− ann = snd . pm_annotations . tm_parsed_module
72− annotationFileName :: TypecheckedModule -> Maybe FastString
94+ ann = snd . pm_annotations
95+ annotationFileName :: ParsedModule -> Maybe FastString
7396 annotationFileName = fmap srcSpanFile . listToMaybe . realSpans . ann
7497 realSpans :: M.Map SrcSpan [Located a] -> [RealSrcSpan]
7598 realSpans =
@@ -83,5 +106,10 @@ docHeaders :: [RealLocated AnnotationComment]
83106docHeaders = mapMaybe (\(L _ x) -> wrk x)
84107 where
85108 wrk = \case
109+ -- When `Opt_Haddock` is enabled.
86110 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
87115 _ -> Nothing
src/Development/IDE/Types/SpanInfo.hs → src/Development/IDE/Spans/Type.hsrenamed+34−20
@@ -1,63 +1,77 @@
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.
22-- SPDX-License-Identifier: Apache-2.0
33
44-- ORIGINALLY COPIED FROM https://github.com/commercialhaskell/intero
55
66-- | Types used separate to GHCi vanilla.
77
8−module Development.IDE.Types.SpanInfo(
9− SpanInfo(..)
8+module Development.IDE.Spans.Type(
9+ SpansInfo(..)
10+ , SpanInfo(..)
1011 , SpanSource(..)
1112 , getNameM
12− , getSrcSpan
1313 ) where
1414
1515import GHC
16−import Data.Maybe
16+import Control.DeepSeq
1717import OccName
18+import Development.IDE.GHC.Util
19+import Development.IDE.Spans.Common
1820
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
1928
2029-- | Type of some span of source code. Most of these fields are
2130-- unboxed but Haddock doesn't show that.
2231data SpanInfo =
2332 SpanInfo {spaninfoStartLine :: {-# UNPACK #-} !Int
24− -- ^ Start line of the span.
33+ -- ^ Start line of the span, zero-based.
2534 ,spaninfoStartCol :: {-# UNPACK #-} !Int
26− -- ^ Start column of the span.
35+ -- ^ Start column of the span, zero-based.
2736 ,spaninfoEndLine :: {-# UNPACK #-} !Int
28− -- ^ End line of the span (absolute).
37+ -- ^ End line of the span (absolute), zero-based.
2938 ,spaninfoEndCol :: {-# UNPACK #-} !Int
30− -- ^ End column of the span (absolute).
39+ -- ^ End column of the span (absolute), zero-based.
3140 ,spaninfoType :: !(Maybe Type)
32− -- ^ A pretty-printed representation fo the type.
41+ -- ^ A pretty-printed representation for the type.
3342 ,spaninfoSource :: !SpanSource
3443 -- ^ The actutal 'Name' associated with the span, if
3544 -- any. This can be useful for accessing a variety of
3645 -- information about the identifier such as module,
3746 -- locality, definition location, etc.
47+ ,spaninfoDocs :: !SpanDoc
48+ -- ^ Documentation for the element
3849 }
3950instance 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 <> ")"]
55+
56+instance NFData SpanInfo where
57+ rnf = rwhnf
58+
4159
4260-- we don't always get a name out so sometimes manually annotating source is more appropriate
4361data SpanSource = Named Name
44− | Span SrcSpan
62+ | SpanS SrcSpan
63+ | Lit String
4564 | NoSource
4665 deriving (Eq)
4766
4867instance Show SpanSource where
4968 show = \case
5069 Named n -> "Named " ++ occNameString (occName n)
51− Span sp -> "Span " ++ show sp
70+ SpanS sp -> "Span " ++ show sp
71+ Lit lit -> "Lit " ++ lit
5272 NoSource -> "NoSource"
5373
5474getNameM :: SpanSource -> Maybe Name
5575getNameM = \case
5676 Named name -> Just name
57− _ -> Nothing
58−
59−getSrcSpan :: SpanSource -> Maybe SrcSpan
60−getSrcSpan = \case
61− NoSource -> Nothing
62− Span sp -> Just sp
63− Named name -> Just $ nameSrcSpan name
77+ _ -> Nothing
src/Development/IDE/State/FileStore.hsdeleted−153
@@ -1,153 +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 TypeFamilies #-}
5−
6−module Development.IDE.State.FileStore(
7− getFileExists, getFileContents,
8− setBufferModified,
9− fileStoreRules
10− ) where
11−
12−
13−
14−import StringBuffer
15−import Development.IDE.Orphans()
16−
17−import qualified Data.Map.Strict as Map
18−import qualified Data.Text as T
19−import Data.Time.Clock
20−import Control.Monad.Extra
21−import qualified System.Directory as Dir
22−import Development.Shake
23−import Development.Shake.Classes
24−import Development.IDE.State.Shake
25−import Control.Concurrent.Extra
26−import Control.Exception
27−import GHC.Generics
28−import System.IO.Error
29−import qualified Data.ByteString.Char8 as BS
30−import qualified StringBuffer as SB
31−import Development.IDE.Types.Diagnostics
32−import Data.Time
33−
34−
35−-- This module stores the changed files in memory, and answers file system questions
36−-- from either the memory changes OR the file system itself
37−
38−type DirtyFiles = Map.Map FilePath (UTCTime, StringBuffer) -- when it was modified, it's current value
39−
40−-- Store the DirtyFiles globally, so we can get at it through setBufferModified
41−newtype GlobalDirtyFiles = GlobalDirtyFiles (Var DirtyFiles)
42−instance IsIdeGlobal GlobalDirtyFiles
43−
44−
45−
46−-- | Get the modification time of a file.
47−type instance RuleResult GetModificationTime = UTCTime
48−
49−-- | Get the contents of a file, either dirty (if the buffer is modified) or from disk.
50−type instance RuleResult GetFileContents = (UTCTime, StringBuffer)
51−
52−-- | Does the file exist.
53−type instance RuleResult GetFileExists = Bool
54−
55−
56−data GetFileExists = GetFileExists
57− deriving (Eq, Show, Generic)
58−instance Hashable GetFileExists
59−instance NFData GetFileExists
60−
61−data GetModificationTime = GetModificationTime
62− deriving (Eq, Show, Generic)
63−instance Hashable GetModificationTime
64−instance NFData GetModificationTime
65−
66−data GetFileContents = GetFileContents
67− deriving (Eq, Show, Generic)
68−instance Hashable GetFileContents
69−instance NFData GetFileContents
70−
71−
72−getFileExistsRule :: Var DirtyFiles -> Rules ()
73−getFileExistsRule dirty =
74− defineEarlyCutoff $ \GetFileExists file -> do
75− alwaysRerun
76− res <- liftIO $ handle (\(_ :: IOException) -> return False) $
77− (Map.member file <$> readVar dirty) ||^
78− Dir.doesFileExist file
79− return (Just $ if res then BS.singleton '1' else BS.empty, ([], Just res))
80−
81−
82−showTimePrecise :: UTCTime -> String
83−showTimePrecise UTCTime{..} = show (toModifiedJulianDay utctDay, diffTimeToPicoseconds utctDayTime)
84−
85−getModificationTimeRule :: Var DirtyFiles -> Rules ()
86−getModificationTimeRule dirty =
87− defineEarlyCutoff $ \GetModificationTime file -> do
88− let wrap time = (Just $ BS.pack $ showTimePrecise time, ([], Just time))
89− alwaysRerun
90− mp <- liftIO $ readVar dirty
91− case Map.lookup file mp of
92− Just (time, _) -> return $ wrap time
93− Nothing -> liftIO $ fmap wrap (Dir.getModificationTime file) `catch` \(e :: IOException) -> do
94− let err | isDoesNotExistError e = "File does not exist: " ++ file
95− | otherwise = "IO error while reading " ++ file ++ ", " ++ displayException e
96− return (Nothing, ([ideErrorText file $ T.pack err], Nothing))
97−
98−
99−getFileContentsRule :: Var DirtyFiles -> Rules ()
100−getFileContentsRule dirty =
101− define $ \GetFileContents file -> do
102− -- need to depend on modification time to introduce a dependency with Cutoff
103− time <- use_ GetModificationTime file
104− res <- liftIO $ ideTryIOException file $ do
105− mp <- readVar dirty
106− case Map.lookup file mp of
107− Just (_, contents) -> return contents
108− Nothing -> hGetStringBuffer file
109− case res of
110− Left err -> return ([err], Nothing)
111− Right contents -> return ([], Just (time, contents))
112−
113−
114−getFileContents :: FilePath -> Action (UTCTime, StringBuffer)
115−getFileContents = use_ GetFileContents
116−
117−getFileExists :: FilePath -> Action Bool
118−getFileExists =
119− -- we deliberately and intentionally wrap the file as an FilePath WITHOUT mkAbsolute
120− -- so that if the file doesn't exist, is on a shared drive that is unmounted etc we get a properly
121− -- cached 'No' rather than an exception in the wrong place
122− use_ GetFileExists
123−
124−
125−fileStoreRules :: Rules ()
126−fileStoreRules = do
127− dirty <- liftIO $ newVar Map.empty
128− addIdeGlobal $ GlobalDirtyFiles dirty
129− getModificationTimeRule dirty
130− getFileContentsRule dirty
131− getFileExistsRule dirty
132−
133−
134−strictPair :: a -> b -> (a, b)
135−strictPair !a !b = (a,b)
136−
137−
138−-- | Notify the compiler service of a modified buffer
139−setBufferModified :: IdeState -> FilePath -> (Maybe T.Text, UTCTime) -> IO ()
140−setBufferModified state absFile (mcontents, !time) = do
141− GlobalDirtyFiles envDirtyFiles <- getIdeGlobalState state
142− -- update vars synchronously
143− modifyVar_ envDirtyFiles $ evaluate . case mcontents of
144− Nothing -> Map.delete absFile
145− Just contents -> Map.insert absFile $ strictPair time $ textToStringBuffer contents
146−
147− -- run shake to update results regarding the files of interest
148− void $ shakeRun state []
149−
150−
151−-- would be nice to do this more efficiently...
152−textToStringBuffer :: T.Text -> SB.StringBuffer
153−textToStringBuffer = SB.stringToStringBuffer . T.unpack
src/Development/IDE/State/Rules.hsdeleted−336
@@ -1,336 +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 TypeFamilies #-}
5−{-# LANGUAGE OverloadedStrings #-}
6−{-# LANGUAGE FlexibleInstances #-}
7−{-# LANGUAGE DuplicateRecordFields #-}
8−
9−-- | A Shake implementation of the compiler service, built
10−-- using the "Shaker" abstraction layer for in-memory use.
11−--
12−module Development.IDE.State.Rules(
13− IdeState, GetDependencies(..), GetParsedModule(..), TransitiveDependencies(..),
14− Priority(..),
15− runAction, runActions, useE, usesE,
16− toIdeResultNew, defineNoFile,
17− mainRule,
18− getGhcCore,
19− getAtPoint,
20− getDefinition,
21− getDependencies,
22− getDalfDependencies,
23− fileFromParsedModule
24− ) where
25−
26−import Control.Monad.Except
27−import Control.Monad.Extra (whenJust)
28−import qualified Development.IDE.Functions.Compile as Compile
29−import qualified Development.IDE.Types.Options as Compile
30−import Development.IDE.Functions.DependencyInformation
31−import Development.IDE.Functions.FindImports
32−import Development.IDE.State.FileStore
33−import Development.IDE.Types.Diagnostics as Base
34−import Data.Bifunctor
35−import Data.Either.Extra
36−import Data.Maybe
37−import Data.Foldable
38−import qualified Data.Map.Strict as Map
39−import qualified Data.Set as Set
40−import qualified Data.Text as T
41−import Development.IDE.Functions.GHCError
42−import Development.Shake hiding (Diagnostic, Env, newCache)
43−import Development.IDE.Types.LSP as Compiler
44−import Development.IDE.State.RuleTypes
45−
46−import GHC
47−import Development.IDE.Compat
48−import UniqSupply
49−import Module as M
50−import NameCache
51−
52−import qualified Development.IDE.Functions.AtPoint as AtPoint
53−import Development.IDE.State.Service
54−import Development.IDE.State.Shake
55−
56−-- LEGACY STUFF ON THE OLD STYLE
57−
58−toIdeResultNew :: Either [FileDiagnostic] v -> IdeResult v
59−toIdeResultNew = either (, Nothing) (([],) . Just)
60−
61−-- Convert to a legacy Ide result but dropping dependencies
62−toIdeResultSilent :: Maybe v -> Either [FileDiagnostic] v
63−toIdeResultSilent val = maybe (Left []) Right val
64−
65−
66−defineNoFile :: IdeRule k v => (k -> Action v) -> Rules ()
67−defineNoFile f = define $ \k file -> do
68− if file == "" then do res <- f k; return ([], Just res) else
69− fail $ "Rule " ++ show k ++ " should always be called with the empty string for a file"
70−
71−
72−------------------------------------------------------------
73−-- Exposed API
74−
75−
76−-- | Get GHC Core for the supplied file.
77−getGhcCore :: FilePath -> Action (Maybe [CoreModule])
78−getGhcCore file = eitherToMaybe <$> runExceptT (coresForFile file)
79−
80−-- | Generate the GHC Core for the supplied file and its dependencies.
81−coresForFile :: FilePath -> ExceptT [FileDiagnostic] Action [CoreModule]
82−coresForFile file = do
83− files <- transitiveModuleDeps <$> useE GetDependencies file
84− pms <- usesE GetParsedModule $ files ++ [file]
85− fs <- liftIO
86− . mapM fileFromParsedModule
87− $ pms
88− cores <- usesE GenerateCore fs
89− pure (map Compile.gmCore cores)
90−
91−
92−
93−-- | Get all transitive file dependencies of a given module.
94−-- Does not include the file itself.
95−getDependencies :: FilePath -> Action (Maybe [FilePath])
96−getDependencies file =
97− eitherToMaybe <$>
98− (runExceptT $ transitiveModuleDeps <$> useE GetDependencies file)
99−
100−getDalfDependencies :: FilePath -> Action (Maybe [InstalledUnitId])
101−getDalfDependencies file =
102− eitherToMaybe <$>
103− (runExceptT $ transitivePkgDeps <$> useE GetDependencies file)
104−
105−-- | Documentation at point.
106−getAtPoint :: FilePath -> Position -> Action (Maybe (Maybe Range, [HoverText]))
107−getAtPoint file pos = do
108− fmap (either (const Nothing) id) . runExceptT $ getAtPointForFile file pos
109−
110−-- | Goto Definition.
111−getDefinition :: FilePath -> Position -> Action (Maybe Location)
112−getDefinition file pos = do
113− fmap (either (const Nothing) id) . runExceptT $ getDefinitionForFile file pos
114−
115−
116−------------------------------------------------------------
117−-- Internal Actions
118−
119−useE
120− :: IdeRule k v
121− => k -> FilePath -> ExceptT [FileDiagnostic] Action v
122−useE k = ExceptT . fmap toIdeResultSilent . use k
123−
124−-- picks the first error
125−usesE
126− :: IdeRule k v
127− => k -> [FilePath] -> ExceptT [FileDiagnostic] Action [v]
128−usesE k = ExceptT . fmap (mapM toIdeResultSilent) . uses k
129−
130−-- | Try to get hover text for the name under point.
131−getAtPointForFile
132− :: FilePath
133− -> Position
134− -> ExceptT [FileDiagnostic] Action (Maybe (Maybe Range, [HoverText]))
135−getAtPointForFile file pos = do
136− files <- transitiveModuleDeps <$> useE GetDependencies file
137− tms <- usesE TypeCheck (file : files)
138− spans <- useE GetSpanInfo file
139− return $ AtPoint.atPoint (map Compile.tmrModule tms) spans pos
140−
141−getDefinitionForFile :: FilePath -> Position -> ExceptT [FileDiagnostic] Action (Maybe Location)
142−getDefinitionForFile file pos = do
143− spans <- useE GetSpanInfo file
144− pkgState <- useE GhcSession ""
145− opts <- lift getOpts
146− lift $ AtPoint.gotoDefinition opts pkgState spans pos
147−
148−getOpts :: Action Compile.IdeOptions
149−getOpts = envOptions <$> getServiceEnv
150−
151−------------------------------------------------------------
152−-- Rules
153−-- These typically go from key to value and are oracles.
154−
155−-- TODO (MK) This should be independent of DAML or move out of haskell-ide-core.
156−-- | We build artefacts based on the following high-to-low priority order.
157−data Priority
158− = PriorityTypeCheck
159− | PriorityGenerateDalf
160− | PriorityFilesOfInterest
161− deriving (Eq, Ord, Show, Enum)
162−
163−
164−getParsedModuleRule :: Rules ()
165−getParsedModuleRule =
166− define $ \GetParsedModule file -> do
167− contents <- getFileContents file
168− packageState <- use_ GhcSession ""
169− opt <- getOpts
170− liftIO $ Compile.parseModule opt packageState file contents
171−
172−getLocatedImportsRule :: Rules ()
173−getLocatedImportsRule =
174− define $ \GetLocatedImports file -> do
175− pm <- use_ GetParsedModule file
176− let ms = pm_mod_summary pm
177− let imports = ms_textual_imps ms
178− packageState <- use_ GhcSession ""
179− opt <- getOpts
180− dflags <- liftIO $ Compile.getGhcDynFlags opt pm packageState
181− xs <- forM imports $ \(mbPkgName, modName) ->
182− (modName, ) <$> locateModule dflags (Compile.optExtensions opt) getFileExists modName mbPkgName
183− return (concat $ lefts $ map snd xs, Just $ map (second eitherToMaybe) xs)
184−
185−
186−-- | Given a target file path, construct the raw dependency results by following
187−-- imports recursively.
188−rawDependencyInformation :: FilePath -> ExceptT [FileDiagnostic] Action RawDependencyInformation
189−rawDependencyInformation f = go (Set.singleton f) Map.empty Map.empty
190− where go fs !modGraph !pkgs =
191− case Set.minView fs of
192− Nothing -> pure (RawDependencyInformation modGraph pkgs)
193− Just (f, fs) -> do
194− importsOrErr <- lift $ use GetLocatedImports f
195− case importsOrErr of
196− Nothing ->
197− let modGraph' = Map.insert f (Left ModuleParseError) modGraph
198− in go fs modGraph' pkgs
199− Just imports -> do
200− packageState <- lift $ use_ GhcSession ""
201− opt <- lift getOpts
202− modOrPkgImports <- forM imports $ \imp -> do
203− case imp of
204− (_modName, Just (PackageImport pkg)) -> do
205− pkgs <- ExceptT $ liftIO $ Compile.computePackageDeps opt packageState pkg
206− pure $ Right $ pkg:pkgs
207− (modName, Just (FileImport absFile)) -> pure $ Left (modName, Just absFile)
208− (modName, Nothing) -> pure $ Left (modName, Nothing)
209− let (modImports, pkgImports) = partitionEithers modOrPkgImports
210− let newFiles = Set.fromList (mapMaybe snd modImports) Set.\\ Map.keysSet modGraph
211− modGraph' = Map.insert f (Right modImports) modGraph
212− pkgs' = Map.insert f (Set.fromList $ concat pkgImports) pkgs
213− go (fs `Set.union` newFiles) modGraph' pkgs'
214−
215−getDependencyInformationRule :: Rules ()
216−getDependencyInformationRule =
217− define $ \GetDependencyInformation file -> fmap toIdeResultNew $ runExceptT $ do
218− rawDepInfo <- rawDependencyInformation file
219− pure $ processDependencyInformation rawDepInfo
220−
221−reportImportCyclesRule :: Rules ()
222−reportImportCyclesRule =
223− define $ \ReportImportCycles file -> fmap toIdeResultNew $ runExceptT $ do
224− DependencyInformation{..} <- useE GetDependencyInformation file
225− whenJust (Map.lookup file depErrorNodes) $ \errs -> do
226− let cycles = mapMaybe (cycleErrorInFile file) (toList errs)
227− when (not $ null cycles) $ do
228− -- Convert cycles of files into cycles of module names
229− diags <- forM cycles $ \(imp, files) -> do
230− modNames <- mapM getModuleName files
231− pure $ toDiag imp modNames
232− throwError diags
233− where cycleErrorInFile f (PartOfCycle imp fs)
234− | f `elem` fs = Just (imp, fs)
235− cycleErrorInFile _ _ = Nothing
236− toDiag imp mods = (fp ,) $ Diagnostic
237− { _range = (_range :: Location -> Range) loc
238− , _severity = Just DsError
239− , _source = Just "Import cycle detection"
240− , _message = "Cyclic module dependency between " <> showCycle mods
241− , _code = Nothing
242− , _relatedInformation = Nothing
243− }
244− where loc = srcSpanToLocation (getLoc imp)
245− fp = srcSpanToFilename (getLoc imp)
246− getModuleName file = do
247− pm <- useE GetParsedModule file
248− pure (moduleNameString . moduleName . ms_mod $ pm_mod_summary pm)
249− showCycle mods = T.intercalate ", " (map T.pack mods)
250−
251−-- returns all transitive dependencies in topological order.
252−-- NOTE: result does not include the argument file.
253−getDependenciesRule :: Rules ()
254−getDependenciesRule =
255− define $ \GetDependencies file -> do
256− depInfo@DependencyInformation{..} <- use_ GetDependencyInformation file
257− let allFiles = Map.keys depModuleDeps <> Map.keys depErrorNodes
258− _ <- uses_ ReportImportCycles allFiles
259− return ([], transitiveDeps depInfo file)
260−
261−-- Source SpanInfo is used by AtPoint and Goto Definition.
262−getSpanInfoRule :: Rules ()
263−getSpanInfoRule =
264− define $ \GetSpanInfo file -> do
265− pm <- use_ GetParsedModule file
266− tc <- use_ TypeCheck file
267− imports <- use_ GetLocatedImports file
268− packageState <- use_ GhcSession ""
269− opt <- getOpts
270− x <- liftIO $ Compile.getSrcSpanInfos opt pm packageState (fileImports imports) tc
271− return ([], Just x)
272−
273−-- Typechecks a module.
274−typeCheckRule :: Rules ()
275−typeCheckRule =
276− define $ \TypeCheck file -> do
277− pm <- use_ GetParsedModule file
278− deps <- use_ GetDependencies file
279− tms <- uses_ TypeCheck (transitiveModuleDeps deps)
280− setPriority PriorityTypeCheck
281− packageState <- use_ GhcSession ""
282− opt <- getOpts
283− liftIO $ Compile.typecheckModule opt pm packageState tms pm
284−
285−
286−generateCoreRule :: Rules ()
287−generateCoreRule =
288− define $ \GenerateCore file -> do
289− deps <- use_ GetDependencies file
290− (tm:tms) <- uses_ TypeCheck (file:transitiveModuleDeps deps)
291− let pm = tm_parsed_module . Compile.tmrModule $ tm
292− setPriority PriorityGenerateDalf
293− packageState <- use_ GhcSession ""
294− opt <- getOpts
295− liftIO $ Compile.compileModule opt pm packageState tms tm
296−
297−loadGhcSession :: Rules ()
298−loadGhcSession =
299− defineNoFile $ \GhcSession -> do
300− opts <- envOptions <$> getServiceEnv
301− Compile.optGhcSession opts
302−
303−
304−getHieFileRule :: Rules ()
305−getHieFileRule =
306− defineNoFile $ \(GetHieFile f) -> do
307− u <- liftIO $ mkSplitUniqSupply 'a'
308− let nameCache = initNameCache u []
309− liftIO $ fmap fst $ readHieFile nameCache f
310−
311−-- | A rule that wires per-file rules together
312−mainRule :: Rules ()
313−mainRule = do
314− getParsedModuleRule
315− getLocatedImportsRule
316− getDependencyInformationRule
317− reportImportCyclesRule
318− getDependenciesRule
319− typeCheckRule
320− getSpanInfoRule
321− generateCoreRule
322− loadGhcSession
323− getHieFileRule
324−
325−------------------------------------------------------------
326−
327−fileFromParsedModule :: ParsedModule -> IO FilePath
328−fileFromParsedModule = pure . ms_hspp_file . pm_mod_summary
329−
330−fileImports ::
331− [(Located ModuleName, Maybe Import)]
332− -> [(Located ModuleName, Maybe FilePath)]
333−fileImports = mapMaybe $ \case
334− (modName, Nothing) -> Just (modName, Nothing)
335− (modName, Just (FileImport absFile)) -> Just (modName, Just absFile)
336− (_modName, Just (PackageImport _pkg)) -> Nothing
src/Development/IDE/State/Service.hsdeleted−120
@@ -1,120 +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 TypeFamilies #-}
5−{-# LANGUAGE OverloadedStrings #-}
6−{-# LANGUAGE FlexibleInstances #-}
7−
8−-- | A Shake implementation of the compiler service, built
9−-- using the "Shaker" abstraction layer for in-memory use.
10−--
11−module Development.IDE.State.Service(
12− Env(..),
13− getServiceEnv,
14− IdeState, initialise, shutdown,
15− runAction, runActions,
16− setFilesOfInterest,
17− writeProfile,
18− getDiagnostics, unsafeClearDiagnostics,
19− logDebug, logSeriousError
20− ) where
21−
22−import Control.Concurrent.Extra
23−import Control.Monad.Except
24−import Development.IDE.Types.Options (IdeOptions(..))
25−import Development.IDE.State.FileStore
26−import qualified Development.IDE.Logger as Logger
27−import Data.Maybe
28−import Data.Set (Set)
29−import qualified Data.Set as Set
30−import Development.IDE.Functions.GHCError
31−import Development.Shake hiding (Diagnostic, Env, newCache)
32−import Development.IDE.Types.LSP as Compiler
33−
34−import UniqSupply
35−
36−import Development.IDE.State.Shake
37−
38−
39−-- | Environment threaded through the Shake actions.
40−data Env = Env
41− { envOptions :: IdeOptions
42− -- ^ Compiler options.
43− , envOfInterestVar :: Var (Set FilePath)
44− -- ^ The files of interest.
45− , envUniqSupplyVar :: Var UniqSupply
46− -- ^ The unique supply of names used by the compiler.
47− }
48−instance IsIdeGlobal Env
49−
50−
51−mkEnv :: IdeOptions -> IO Env
52−mkEnv options = do
53− ofInterestVar <- newVar Set.empty
54− uniqSupplyVar <- mkSplitUniqSupply 'a' >>= newVar
55− return Env
56− { envOptions = options
57− , envOfInterestVar = ofInterestVar
58− , envUniqSupplyVar = uniqSupplyVar
59− }
60−
61−getDiagnostics :: IdeState -> IO [FileDiagnostic]
62−getDiagnostics = getAllDiagnostics
63−
64−unsafeClearDiagnostics :: IdeState -> IO ()
65−unsafeClearDiagnostics = unsafeClearAllDiagnostics
66−
67−
68−------------------------------------------------------------
69−-- Exposed API
70−
71−-- | Initialise the Compiler Service.
72−initialise :: Rules ()
73− -> Maybe (Event -> IO ())
74− -> Logger.Handle
75− -> IdeOptions
76− -> IO IdeState
77−initialise mainRule toDiags logger options =
78− shakeOpen
79− (fromMaybe (const $ pure ()) toDiags)
80− logger
81− (setProfiling options $
82− shakeOptions { shakeThreads = optThreads options
83− , shakeFiles = "/dev/null"
84− }) $ do
85− addIdeGlobal =<< liftIO (mkEnv options)
86− fileStoreRules
87− mainRule
88−
89−writeProfile :: IdeState -> FilePath -> IO ()
90−writeProfile = shakeProfile
91−
92−setProfiling :: IdeOptions -> ShakeOptions -> ShakeOptions
93−setProfiling opts shakeOpts =
94− maybe shakeOpts (\p -> shakeOpts { shakeReport = [p], shakeTimings = True }) (optShakeProfiling opts)
95−
96−-- | Shutdown the Compiler Service.
97−shutdown :: IdeState -> IO ()
98−shutdown = shakeShut
99−
100−-- | Run a single action using the supplied service.
101−runAction :: IdeState -> Action a -> IO a
102−runAction service action = head <$> runActions service [action]
103−
104−-- | Run a list of actions in parallel using the supplied service.
105−runActions :: IdeState -> [Action a] -> IO [a]
106−runActions x = join . shakeRun x
107−
108−
109−-- | Set the files-of-interest which will be built and kept-up-to-date.
110−setFilesOfInterest :: IdeState -> Set FilePath -> IO ()
111−setFilesOfInterest state files = do
112− Env{..} <- getIdeGlobalState state
113− -- update vars synchronously
114− modifyVar_ envOfInterestVar $ const $ return files
115−
116− -- run shake to update results regarding the files of interest
117− void $ shakeRun state []
118−
119−getServiceEnv :: Action Env
120−getServiceEnv = getIdeGlobalAction
src/Development/IDE/State/Shake.hsdeleted−426
@@ -1,426 +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 ExistentialQuantification #-}
5−{-# LANGUAGE TypeFamilies #-}
6−{-# LANGUAGE ConstraintKinds #-}
7−{-# LANGUAGE DuplicateRecordFields #-}
8−{-# LANGUAGE OverloadedStrings #-}
9−
10−-- | A Shake implementation of the compiler service.
11−--
12−-- There are two primary locations where data lives, and both of
13−-- these contain much the same data:
14−--
15−-- * The Shake database (inside 'shakeDb') stores a map of shake keys
16−-- to shake values. In our case, these are all of type 'Q' to 'A'.
17−-- During a single run all the values in the Shake database are consistent
18−-- so are used in conjunction with each other, e.g. in 'uses'.
19−--
20−-- * The 'Values' type stores a map of keys to values. These values are
21−-- always stored as real Haskell values, whereas Shake serialises all 'A' values
22−-- between runs. To deserialise a Shake value, we just consult Values.
23−-- Additionally, Values can be used in an inconsistent way, for example
24−-- useStale.
25−module Development.IDE.State.Shake(
26− IdeState,
27− IdeRule, IdeResult,
28− shakeOpen, shakeShut,
29− shakeRun,
30− shakeProfile,
31− useStale,
32− use, uses,
33− use_, uses_,
34− define, defineEarlyCutoff,
35− getAllDiagnostics, unsafeClearAllDiagnostics,
36− reportSeriousError, reportSeriousErrorDie,
37− IsIdeGlobal, addIdeGlobal, getIdeGlobalState, getIdeGlobalAction,
38− garbageCollect,
39− setPriority,
40− sendEvent,
41− Development.IDE.State.Shake.logDebug,
42− Development.IDE.State.Shake.logSeriousError,
43− ) where
44−
45−import Development.Shake
46−import Development.Shake.Database
47−import Development.Shake.Classes
48−import Development.Shake.Rule
49−import qualified Data.HashMap.Strict as Map
50−import qualified Data.ByteString.Char8 as BS
51−import Data.Dynamic
52−import Data.Maybe
53−import Data.Either
54−import Data.List.Extra
55−import qualified Data.Text as T
56−import Development.IDE.Logger as Logger
57−import Development.IDE.Types.LSP
58−import Development.IDE.Types.Diagnostics
59−import Control.Concurrent.Extra
60−import Control.Exception
61−import Control.DeepSeq
62−import System.Time.Extra
63−import Data.Typeable
64−import Data.Tuple.Extra
65−import System.Directory
66−import System.FilePath
67−import qualified Development.Shake as Shake
68−import Control.Monad.Extra
69−import qualified Data.Set as Set
70−import Data.Time
71−import System.IO.Unsafe
72−import Numeric.Extra
73−
74−
75−
76−-- information we stash inside the shakeExtra field
77−data ShakeExtras = ShakeExtras
78− {eventer :: Event -> IO ()
79− ,logger :: Logger.Handle
80− ,globals :: Var (Map.HashMap TypeRep Dynamic)
81− ,state :: Var Values
82− }
83−
84−getShakeExtras :: Action ShakeExtras
85−getShakeExtras = do
86− Just x <- getShakeExtra @ShakeExtras
87− return x
88−
89−getShakeExtrasRules :: Rules ShakeExtras
90−getShakeExtrasRules = do
91− Just x <- getShakeExtraRules @ShakeExtras
92− return x
93−
94−
95−
96−class Typeable a => IsIdeGlobal a where
97−
98−addIdeGlobal :: IsIdeGlobal a => a -> Rules ()
99−addIdeGlobal x@(typeOf -> ty) = do
100− ShakeExtras{globals} <- getShakeExtrasRules
101− liftIO $ modifyVar_ globals $ \mp -> case Map.lookup ty mp of
102− Just _ -> error $ "Can't addIdeGlobal twice on the same type, got " ++ show ty
103− Nothing -> return $! Map.insert ty (toDyn x) mp
104−
105−
106−getIdeGlobalExtras :: forall a . IsIdeGlobal a => ShakeExtras -> IO a
107−getIdeGlobalExtras ShakeExtras{globals} = do
108− Just x <- Map.lookup (typeRep (Proxy :: Proxy a)) <$> readVar globals
109− return $ fromDyn x $ error "Serious error, corrupt globals"
110−
111−getIdeGlobalAction :: forall a . IsIdeGlobal a => Action a
112−getIdeGlobalAction = liftIO . getIdeGlobalExtras =<< getShakeExtras
113−
114−getIdeGlobalState :: forall a . IsIdeGlobal a => IdeState -> IO a
115−getIdeGlobalState = getIdeGlobalExtras . shakeExtras
116−
117−
118−-- | The state of the all values - nested so you can easily find all errors at a given file.
119−type Values =
120− Map.HashMap FilePath
121− (Map.HashMap Key
122− (IdeResult Dynamic)
123− )
124−
125−
126−-- | Key type
127−data Key = forall k . (Typeable k, Hashable k, Eq k, Show k) => Key k
128−
129−instance Show Key where
130− show (Key k) = show k
131−
132−instance Eq Key where
133− Key k1 == Key k2 | Just k2' <- cast k2 = k1 == k2'
134− | otherwise = False
135−
136−instance Hashable Key where
137− hashWithSalt salt (Key key) = hashWithSalt salt key
138−
139−-- | The result of an IDE operation. Warnings and errors are in the Diagnostic,
140−-- and a value is in the Maybe. For operations that throw an error you
141−-- expect a non-empty list of diagnostics, at least one of which is an error,
142−-- and a Nothing. For operations that succeed you expect perhaps some warnings
143−-- and a Just. For operations that depend on other failing operations you may
144−-- get empty diagnostics and a Nothing, to indicate this phase throws no fresh
145−-- errors but still failed.
146−--
147−-- A rule on a file should only return diagnostics for that given file. It should
148−-- not propagate diagnostic errors through multiple phases.
149−type IdeResult v = ([FileDiagnostic], Maybe v)
150−
151−type IdeRule k v =
152− ( Shake.RuleResult k ~ v
153− , Show k
154− , Typeable k
155− , NFData k
156− , Hashable k
157− , Eq k
158− , Show v
159− , Typeable v
160− , NFData v
161− )
162−
163−
164−-- | A Shake database plus persistent store. Can be thought of as storing
165−-- mappings from @(FilePath, k)@ to @RuleResult k@.
166−data IdeState = IdeState
167− {shakeDb :: ShakeDatabase
168− ,shakeAbort :: Var (IO ()) -- close whoever was running last
169− ,shakeClose :: IO ()
170− ,shakeExtras :: ShakeExtras
171− }
172−
173−
174−profileDir :: Maybe FilePath
175−profileDir = Nothing -- set to Just the directory you want profile reports to appear in
176−
177−
178−-- This is debugging code that generates a series of profiles, if the Boolean is true
179−shakeRunDatabaseProfile :: ShakeDatabase -> [Action a] -> IO [a]
180−shakeRunDatabaseProfile shakeDb acts = do
181− (time, (res,_)) <- duration $ shakeRunDatabase shakeDb acts
182− whenJust profileDir $ \dir -> do
183− count <- modifyVar profileCounter $ \x -> let y = x+1 in return (y,y)
184− let file = "ide-" ++ profileStartTime ++ "-" ++ takeEnd 5 ("0000" ++ show count) ++ "-" ++ showDP 2 time <.> "html"
185− shakeProfileDatabase shakeDb $ dir </> file
186− return res
187− where
188−
189−{-# NOINLINE profileStartTime #-}
190−profileStartTime :: String
191−profileStartTime = unsafePerformIO $ formatTime defaultTimeLocale "%Y%m%d-%H%M%S" <$> getCurrentTime
192−
193−{-# NOINLINE profileCounter #-}
194−profileCounter :: Var Int
195−profileCounter = unsafePerformIO $ newVar 0
196−
197−setValues :: IdeRule k v
198− => Var Values
199− -> k
200− -> FilePath
201− -> IdeResult v
202− -> IO (Maybe [FileDiagnostic], [FileDiagnostic]) -- ^ (before, after)
203−setValues state key file val = modifyVar state $ \inVal -> do
204− let k = Key key
205− outVal = Map.insertWith Map.union file (Map.singleton k $ second (fmap toDyn) val) inVal
206− f = concatMap fst . Map.elems
207− return (outVal, (f <$> Map.lookup file inVal, f $ outVal Map.! file))
208−
209−-- | The outer Maybe is Nothing if this function hasn't been computed before
210−-- the inner Maybe is Nothing if the result of the previous computation failed to produce
211−-- a value
212−getValues :: forall k v. IdeRule k v => Var Values -> k -> FilePath -> IO (Maybe (Maybe v))
213−getValues state key file = do
214− vs <- readVar state
215− return $ do
216− f <- Map.lookup file vs
217− v <- Map.lookup (Key key) f
218− pure $ fmap (fromJust . fromDynamic @v) $ snd v
219−
220−-- | Open a 'IdeState', should be shut using 'shakeShut'.
221−shakeOpen :: (Event -> IO ()) -- ^ diagnostic handler
222− -> Logger.Handle
223− -> ShakeOptions
224− -> Rules ()
225− -> IO IdeState
226−shakeOpen diags shakeLogger opts rules = do
227− shakeExtras <- ShakeExtras diags shakeLogger <$> newVar Map.empty <*> newVar Map.empty
228− (shakeDb, shakeClose) <- shakeOpenDatabase opts{shakeExtra = addShakeExtra shakeExtras $ shakeExtra opts} rules
229− shakeAbort <- newVar $ return ()
230− shakeDb <- shakeDb
231− return IdeState{..}
232−
233−shakeProfile :: IdeState -> FilePath -> IO ()
234−shakeProfile IdeState{..} = shakeProfileDatabase shakeDb
235−
236−shakeShut :: IdeState -> IO ()
237−shakeShut = shakeClose
238−
239−-- | Spawn immediately, add an action to collect the results syncronously.
240−-- If you are already inside a call to shakeRun that will be aborted with an exception.
241−shakeRun :: IdeState -> [Action a] -> IO (IO [a])
242−-- FIXME: If there is already a shakeRun queued up and waiting to send me a kill, I should probably
243−-- not even start, which would make issues with async exceptions less problematic.
244−shakeRun IdeState{shakeExtras=ShakeExtras{..}, ..} acts = modifyVar shakeAbort $ \stop -> do
245− (stopTime,_) <- duration stop
246− Logger.logDebug logger $ T.pack $ "Starting shakeRun (aborting the previous one took " ++ showDuration stopTime ++ ")"
247− bar <- newBarrier
248− start <- offsetTime
249− thread <- forkFinally (shakeRunDatabaseProfile shakeDb acts) $ \res -> do
250− signalBarrier bar res
251− runTime <- start
252− Logger.logDebug logger $ T.pack $
253− "Finishing shakeRun (took " ++ showDuration runTime ++ ", " ++ (if isLeft res then "exception" else "completed") ++ ")"
254− -- important: we send an async exception to the thread, then wait for it to die, before continuing
255− return (do killThread thread; void $ waitBarrier bar, either throwIO return =<< waitBarrier bar)
256−
257−-- | Use the last stale value, if it's ever been computed.
258−useStale
259− :: IdeRule k v
260− => IdeState -> k -> FilePath -> IO (Maybe v)
261−useStale IdeState{shakeExtras=ShakeExtras{state}} k fp =
262− join <$> getValues state k fp
263−
264−
265−getAllDiagnostics :: IdeState -> IO [FileDiagnostic]
266−getAllDiagnostics IdeState{shakeExtras = ShakeExtras{state}} = do
267− val <- readVar state
268− return $ concatMap (concatMap fst . Map.elems) $ Map.elems val
269−
270−-- | FIXME: This function is temporary! Only required because the files of interest doesn't work
271−unsafeClearAllDiagnostics :: IdeState -> IO ()
272−unsafeClearAllDiagnostics IdeState{shakeExtras = ShakeExtras{state}} = modifyVar_ state $
273− return . Map.map (Map.map (\(_, x) -> ([], x)))
274−
275−-- | Clear the results for all files that do not match the given predicate.
276−garbageCollect :: (FilePath -> Bool) -> Action ()
277−garbageCollect keep = do
278− ShakeExtras{state} <- getShakeExtras
279− liftIO $ modifyVar_ state $ return . Map.filterWithKey (\file _ -> keep file)
280−
281−define
282− :: IdeRule k v
283− => (k -> FilePath -> Action (IdeResult v)) -> Rules ()
284−define op = defineEarlyCutoff $ \k v -> (Nothing,) <$> op k v
285−
286−use :: IdeRule k v
287− => k -> FilePath -> Action (Maybe v)
288−use key file = head <$> uses key [file]
289−
290−use_ :: IdeRule k v => k -> FilePath -> Action v
291−use_ key file = head <$> uses_ key [file]
292−
293−uses_ :: IdeRule k v => k -> [FilePath] -> Action [v]
294−uses_ key files = do
295− res <- uses key files
296− case sequence res of
297− Nothing -> liftIO $ throwIO BadDependency
298− Just v -> return v
299−
300−reportSeriousError :: String -> Action ()
301−reportSeriousError t = do
302− ShakeExtras{logger} <- getShakeExtras
303− liftIO $ Logger.logSeriousError logger $ T.pack t
304−
305−reportSeriousErrorDie :: String -> Action a
306−reportSeriousErrorDie t = do
307− ShakeExtras{logger} <- getShakeExtras
308− liftIO $ Logger.logSeriousError logger $ T.pack t
309− fail t
310−
311−
312−-- | When we depend on something that reported an error, and we fail as a direct result, throw BadDependency
313−-- which short-circuits the rest of the action
314−data BadDependency = BadDependency deriving Show
315−instance Exception BadDependency
316−
317−isBadDependency :: SomeException -> Bool
318−isBadDependency x
319− | Just (x :: ShakeException) <- fromException x = isBadDependency $ shakeExceptionInner x
320− | Just (_ :: BadDependency) <- fromException x = True
321− | otherwise = False
322−
323−
324−newtype Q k = Q (k, FilePath)
325− deriving (Eq,Hashable,NFData)
326−
327−-- Using Database we don't need Binary instances for keys
328−instance Binary (Q k) where
329− put _ = return ()
330− get = fail "Binary.get not defined for type Development.IDE.State.Shake.Q"
331−
332−instance Show k => Show (Q k) where
333− show (Q (k, file)) = show k ++ "; " ++ file
334−
335−-- | Invariant: the 'v' must be in normal form (fully evaluated).
336−-- Otherwise we keep repeatedly 'rnf'ing values taken from the Shake database
337−data A v = A (Maybe v) (Maybe BS.ByteString)
338− deriving Show
339−
340−instance NFData (A v) where rnf (A v x) = v `seq` rnf x
341−
342−-- In the Shake database we only store one type of key/result pairs,
343−-- namely Q (question) / A (answer).
344−type instance RuleResult (Q k) = A (RuleResult k)
345−
346−
347−-- | Compute the value
348−uses :: IdeRule k v
349− => k -> [FilePath] -> Action [Maybe v]
350−uses key files = map (\(A value _) -> value) <$> apply (map (Q . (key,)) files)
351−
352−defineEarlyCutoff
353− :: IdeRule k v
354− => (k -> FilePath -> Action (Maybe BS.ByteString, IdeResult v))
355− -> Rules ()
356−defineEarlyCutoff op = addBuiltinRule noLint noIdentity $ \(Q (key, file)) old mode -> do
357− ShakeExtras{state} <- getShakeExtras
358− val <- case old of
359− Just old | mode == RunDependenciesSame -> do
360− v <- liftIO $ getValues state key file
361− case v of
362− Just v -> return $ Just $ RunResult ChangedNothing old $ A v (unwrap old)
363− _ -> return Nothing
364− _ -> return Nothing
365− case val of
366− Just res -> return res
367− Nothing -> do
368− (bs, res) <- actionCatch
369− (do v <- op key file; liftIO $ evaluate $ force v) $
370− \(e :: SomeException) -> pure (Nothing, ([ideErrorText file $ T.pack $ show e | not $ isBadDependency e],Nothing))
371− res <- return $ first (map $ \(_,d) -> (file,d)) res
372−
373− (before, after) <- liftIO $ setValues state key file res
374− updateFileDiagnostics file before after
375− let eq = case (bs, fmap unwrap old) of
376− (Just a, Just (Just b)) -> a == b
377− _ -> False
378− return $ RunResult
379− (if eq then ChangedRecomputeSame else ChangedRecomputeDiff)
380− (wrap bs)
381− $ A (snd res) bs
382− where
383− wrap = maybe BS.empty (BS.cons '_')
384− unwrap x = if BS.null x then Nothing else Just $ BS.tail x
385−
386−
387−updateFileDiagnostics ::
388− FilePath
389− -> Maybe [FileDiagnostic] -- ^ previous results for this file
390− -> [FileDiagnostic] -- ^ current results
391− -> Action ()
392−updateFileDiagnostics afp previousAll currentAll = do
393− -- TODO (MK) We canonicalize to make sure that the two files agree on use of
394− -- / and \ and other shenanigans.
395− -- Once we have finished the migration to haskell-lsp we should make sure that
396− -- this is no longer necessary.
397− afp' <- liftIO $ canonicalizePath afp
398− let filtM diags = do
399− diags' <-
400− filterM
401− (\x -> fmap (== afp') (canonicalizePath $ fst x))
402− diags
403− pure (Set.fromList diags')
404− previous <- liftIO $ traverse filtM previousAll
405− current <- liftIO $ filtM currentAll
406− when (Just current /= previous) $
407− sendEvent $ EventFileDiagnostics $ (afp, map snd $ Set.toList current)
408−
409−
410−setPriority :: (Enum a) => a -> Action ()
411−setPriority p =
412− deprioritize (fromIntegral . negate $ fromEnum p)
413−
414−sendEvent :: Event -> Action ()
415−sendEvent e = do
416− ShakeExtras{eventer} <- getShakeExtras
417− liftIO $ eventer e
418−
419−-- | bit of an odd signature because we're trying to remove priority
420−sl :: (Handle -> T.Text -> IO ()) -> IdeState -> T.Text -> IO ()
421−sl f IdeState{shakeExtras=ShakeExtras{logger}} p = f logger p
422−
423−logDebug, logSeriousError
424− :: IdeState -> T.Text -> IO ()
425−logDebug = sl Logger.logDebug
426−logSeriousError = sl Logger.logSeriousError
src/Development/IDE/Types/Diagnostics.hs+84−128
@@ -1,126 +1,74 @@
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.
22-- SPDX-License-Identifier: Apache-2.0
33
4−{-# LANGUAGE FlexibleInstances #-}
5−{-# LANGUAGE OverloadedStrings #-}
6−{-# LANGUAGE DuplicateRecordFields #-}
7−{-# LANGUAGE BlockArguments #-}
4+
85module Development.IDE.Types.Diagnostics (
96 LSP.Diagnostic(..),
10− FileDiagnostics,
7+ ShowDiagnostic(..),
118 FileDiagnostic,
12− Location(..),
13− Range(..),
9+ IdeResult,
1410 LSP.DiagnosticSeverity(..),
15− Position(..),
1611 DiagnosticStore,
17− DiagnosticRelatedInformation(..),
1812 List(..),
19− StoreItem(..),
20− Uri(..),
21− noLocation,
22− noRange,
23− noFilePath,
2413 ideErrorText,
25− ideErrorPretty,
26− errorDiag,
27− ideTryIOException,
14+ ideErrorWithSource,
2815 showDiagnostics,
2916 showDiagnosticsColored,
30− prettyDiagnosticStore,
31− defDiagnostic,
32− addDiagnostics,
33− filterSeriousErrors,
34− filePathToUri,
35− getDiagnosticsFromStore
3617 ) where
3718
38−import Control.Exception
39−import Data.Either.Combinators
19+import Control.DeepSeq
4020import Data.Maybe as Maybe
41−import Data.Foldable
42−import qualified Data.Map as Map
4321import qualified Data.Text as T
44−import Data.Text.Prettyprint.Doc.Syntax
45−import qualified Text.PrettyPrint.Annotated.HughesPJClass as Pretty
46−import Language.Haskell.LSP.Types as LSP (
22+import Data.Text.Prettyprint.Doc
23+import Language.Haskell.LSP.Types as LSP (DiagnosticSource,
4724 DiagnosticSeverity(..)
4825 , Diagnostic(..)
49− , filePathToUri
50− , uriToFilePath
5126 , List(..)
52− , DiagnosticRelatedInformation(..)
53− , Uri(..)
5427 )
5528import Language.Haskell.LSP.Diagnostics
29+import Data.Text.Prettyprint.Doc.Render.Text
30+import qualified Data.Text.Prettyprint.Doc.Render.Terminal as Terminal
31+import Data.Text.Prettyprint.Doc.Render.Terminal (Color(..), color)
5632
5733import Development.IDE.Types.Location
5834
59−ideErrorText :: FilePath -> T.Text -> FileDiagnostic
60−ideErrorText fp = errorDiag fp "Ide Error"
61−
62−ideErrorPretty :: Pretty.Pretty e => FilePath -> e -> FileDiagnostic
63−ideErrorPretty fp = ideErrorText fp . T.pack . Pretty.prettyShow
64−
65−errorDiag :: FilePath -> T.Text -> T.Text -> FileDiagnostic
66−errorDiag fp src msg =
67− (fp, diagnostic noRange LSP.DsError src msg)
68−
69−-- | This is for compatibility with our old diagnostic type
70−diagnostic :: Range
71− -> LSP.DiagnosticSeverity
72− -> T.Text -- ^ source
73− -> T.Text -- ^ message
74− -> LSP.Diagnostic
75−diagnostic rng sev src msg
76− = LSP.Diagnostic {
77− _range = rng,
78− _severity = Just sev,
79− _code = Nothing,
80− _source = Just src,
81− _message = msg,
82− _relatedInformation = Nothing
83− }
84−
85−-- | Any optional field is instantiated to Nothing
86−defDiagnostic ::
87− Range ->
88− T.Text -> -- ^ error message
89− LSP.Diagnostic
90−defDiagnostic _range _message = LSP.Diagnostic {
91− _range
92− , _message
93− , _severity = Nothing
94− , _code = Nothing
95− , _source = Nothing
96− , _relatedInformation = Nothing
97− }
98−
99−filterSeriousErrors ::
100− FilePath ->
101− [LSP.Diagnostic] ->
102− [LSP.Diagnostic]
103−filterSeriousErrors fp =
104− filter (maybe False hasSeriousErrors . LSP._relatedInformation)
105− where
106− hasSeriousErrors :: List DiagnosticRelatedInformation -> Bool
107− hasSeriousErrors (List a) = any ((/=) uri . _uri . _location) a
108− uri = LSP.filePathToUri fp
109−
110−addDiagnostics ::
111− FilePath ->
112− [LSP.Diagnostic] ->
113− DiagnosticStore -> DiagnosticStore
114−addDiagnostics fp diags ds =
115− updateDiagnostics
116− ds
117− (LSP.filePathToUri fp)
118− Nothing $
119− partitionBySource diags
120−
121−ideTryIOException :: FilePath -> IO a -> IO (Either FileDiagnostic a)
122−ideTryIOException fp act =
123− mapLeft (\(e :: IOException) -> ideErrorText fp $ T.pack $ show e) <$> try act
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)
38+
39+ideErrorText :: NormalizedFilePath -> T.Text -> FileDiagnostic
40+ideErrorText = ideErrorWithSource (Just "compiler") (Just DsError)
41+
42+ideErrorWithSource
43+ :: Maybe DiagnosticSource
44+ -> Maybe DiagnosticSeverity
45+ -> a
46+ -> T.Text
47+ -> (a, ShowDiagnostic, Diagnostic)
48+ideErrorWithSource source sev fp msg = (fp, ShowDiag, LSP.Diagnostic {
49+ _range = noRange,
50+ _severity = sev,
51+ _code = Nothing,
52+ _source = source,
53+ _message = msg,
54+ _relatedInformation = Nothing,
55+ _tags = Nothing
56+ })
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
12472
12573-- | Human readable diagnostics for a specific file.
12674--
@@ -128,10 +76,9 @@ ideTryIOException fp act =
12876-- along with the related source location so that we can display the error
12977-- on either the console or in the IDE at the right source location.
13078--
131−type FileDiagnostics = (FilePath, [Diagnostic])
132−type FileDiagnostic = (FilePath, Diagnostic)
79+type FileDiagnostic = (NormalizedFilePath, ShowDiagnostic, Diagnostic)
13380
134−prettyRange :: Range -> Doc SyntaxClass
81+prettyRange :: Range -> Doc Terminal.AnsiStyle
13582prettyRange Range{..} = f _start <> "-" <> f _end
13683 where f Position{..} = pretty (_line+1) <> colon <> pretty _character
13784
@@ -145,42 +92,51 @@ showDiagnosticsColored :: [FileDiagnostic] -> T.Text
14592showDiagnosticsColored = srenderColored . prettyDiagnostics
14693
14794
148−prettyDiagnostics :: [FileDiagnostic] -> Doc SyntaxClass
95+prettyDiagnostics :: [FileDiagnostic] -> Doc Terminal.AnsiStyle
14996prettyDiagnostics = vcat . map prettyDiagnostic
15097
151−prettyDiagnostic :: FileDiagnostic -> Doc SyntaxClass
152−prettyDiagnostic (fp, LSP.Diagnostic{..}) =
98+prettyDiagnostic :: FileDiagnostic -> Doc Terminal.AnsiStyle
99+prettyDiagnostic (fp, sh, LSP.Diagnostic{..}) =
153100 vcat
154− [ slabel_ "File: " $ pretty fp
101+ [ slabel_ "File: " $ pretty (fromNormalizedFilePath fp)
102+ , slabel_ "Hidden: " $ if sh == ShowDiag then "no" else "yes"
155103 , slabel_ "Range: " $ prettyRange _range
156104 , slabel_ "Source: " $ pretty _source
157105 , slabel_ "Severity:" $ pretty $ show sev
158106 , slabel_ "Message: "
159107 $ case sev of
160− LSP.DsError -> annotate ErrorSC
161− LSP.DsWarning -> annotate WarningSC
162− LSP.DsInfo -> annotate InfoSC
163− LSP.DsHint -> annotate HintSC
108+ LSP.DsError -> annotate $ color Red
109+ LSP.DsWarning -> annotate $ color Yellow
110+ LSP.DsInfo -> annotate $ color Blue
111+ LSP.DsHint -> annotate $ color Magenta
164112 $ stringParagraphs _message
165− , slabel_ "Code:" $ pretty _code
166113 ]
167114 where
168115 sev = fromMaybe LSP.DsError _severity
169116
170−prettyDiagnosticStore :: DiagnosticStore -> Doc SyntaxClass
171−prettyDiagnosticStore ds =
172− vcat $
173− map (\(uri, diags) -> prettyFileDiagnostics (fromMaybe noFilePath $ uriToFilePath uri, diags)) $
174− Map.assocs $
175− Map.map getDiagnosticsFromStore ds
176−
177−prettyFileDiagnostics :: FileDiagnostics -> Doc SyntaxClass
178−prettyFileDiagnostics (filePath, diags) =
179− slabel_ "Compiler error in" $ vcat
180− [ slabel_ "File:" $ pretty filePath
181− , slabel_ "Errors:" $ vcat $ map (prettyDiagnostic . (filePath,)) diags
182− ]
183117
184−getDiagnosticsFromStore :: StoreItem -> [Diagnostic]
185−getDiagnosticsFromStore (StoreItem _ diags) =
186− toList =<< Map.elems diags
118+-- | Label a document.
119+slabel_ :: String -> Doc a -> Doc a
120+slabel_ t d = nest 2 $ sep [pretty t, d]
121+
122+-- | The layout options used for the SDK assistant.
123+cliLayout ::
124+ Int
125+ -- ^ Rendering width of the pretty printer.
126+ -> LayoutOptions
127+cliLayout renderWidth = LayoutOptions
128+ { layoutPageWidth = AvailablePerLine renderWidth 0.9
129+ }
130+
131+-- | Render without any syntax annotations
132+srenderPlain :: Doc ann -> T.Text
133+srenderPlain = renderStrict . layoutSmart (cliLayout defaultTermWidth)
134+
135+-- | Render a 'Document' as an ANSII colored string.
136+srenderColored :: Doc Terminal.AnsiStyle -> T.Text
137+srenderColored =
138+ Terminal.renderStrict .
139+ layoutSmart defaultLayoutOptions { layoutPageWidth = AvailablePerLine 100 1.0 }
140+
141+defaultTermWidth :: Int
142+defaultTermWidth = 80
src/Development/IDE/Types/LSP.hsdeleted−61
@@ -1,61 +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−module Development.IDE.Types.LSP
5− ( HoverText(..)
6− , Event(..)
7− , VirtualResource(..)
8− , getHoverTextContent
9− ) where
10−
11−import Control.DeepSeq
12−import qualified Data.Text as T
13−import GHC.Generics
14−
15−import Development.IDE.Types.Diagnostics
16−
17−-- | Different types of content we can show on hover.
18−data HoverText
19− = HoverHeading !T.Text
20− -- ^ A header that explains the content below it.
21− | HoverDamlCode !T.Text
22− -- ^ Highlighted DAML-Code
23− | HoverMarkdown !T.Text
24− -- ^ Markdown text.
25− deriving Show
26−
27−getHoverTextContent :: HoverText -> T.Text
28−getHoverTextContent = \case
29− HoverHeading t -> t
30− HoverDamlCode t -> t
31− HoverMarkdown t -> t
32−
33−-- | Virtual resources
34−data VirtualResource = VRScenario
35− { vrScenarioFile :: !FilePath
36− , vrScenarioName :: !T.Text
37− } deriving (Eq, Ord, Read, Show, Generic)
38− -- ^ VRScenario identifies a scenario in a given file.
39− -- This virtual resource is associated with the HTML result of
40− -- interpreting the corresponding scenario.
41−
42−instance NFData VirtualResource
43−
44−-- | Compiler service events
45−data Event
46− = EventFileDiagnostics !FileDiagnostics
47− -- ^ @EventFileDiagnostics fileDiagnostics@
48− -- How many validations have we finished of how many total
49− -- together with new file diagnostics for a given file.
50− | EventVirtualResourceChanged !VirtualResource T.Text
51− -- ^ @EventVirtualResourceChanged resource contents@ a virtual
52− -- resource @resource@ changed to @contents
53− -- NOTE(JM,MH): Keep the contents lazy as we rely on it in
54− -- 'manageOpenVRs'.
55− | EventFileValidation Int Int
56− -- ^ @EventFileValidation finishedValidations totalValidations @
57− -- How many validations have we finished of how many total.
58− | EventFatalError !T.Text
59− -- ^ @EventFatalError reason@: A fatal error occurred in the compiler and
60− -- the compiler cannot continue.
61− deriving Show
src/Development/IDE/Types/Location.hs+93−59
@@ -1,32 +1,68 @@
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.
22-- SPDX-License-Identifier: Apache-2.0
33
4−{-# LANGUAGE OverloadedStrings #-}
54
65-- | Types and functions for working with source code locations.
76module Development.IDE.Types.Location
8− ( genLocation
9− , inRange
10− , inRangeClosed
11− , isGenLocation
12− , Location(..)
13− , appendLocation
14− , noLocation
7+ ( Location(..)
158 , noFilePath
169 , noRange
1710 , Position(..)
11+ , showPosition
1812 , Range(..)
19− , appendRange
13+ , LSP.Uri(..)
14+ , LSP.NormalizedUri
15+ , LSP.toNormalizedUri
16+ , LSP.fromNormalizedUri
17+ , LSP.NormalizedFilePath
18+ , fromUri
19+ , emptyFilePath
20+ , emptyPathUri
21+ , toNormalizedFilePath'
22+ , LSP.fromNormalizedFilePath
23+ , filePathToUri'
24+ , uriToFilePath'
25+ , readSrcSpan
2026 ) where
2127
22−import Language.Haskell.LSP.Types (Location(..), Range(..), Position(..), Uri(..), filePathToUri)
23−
24−-- | A dummy location to use when location information is missing.
25−noLocation :: Location
26−noLocation = Location
27− { _uri = filePathToUri noFilePath
28− , _range = noRange
29− }
28+import Control.Applicative
29+import Language.Haskell.LSP.Types (Location(..), Range(..), Position(..))
30+import Control.Monad
31+import Data.Hashable (Hashable(hash))
32+import Data.String
33+import FastString
34+import qualified Language.Haskell.LSP.Types as LSP
35+import SrcLoc as GHC
36+import Text.ParserCombinators.ReadP as ReadP
37+import Data.Maybe (fromMaybe)
38+
39+toNormalizedFilePath' :: FilePath -> LSP.NormalizedFilePath
40+-- We want to keep empty paths instead of normalising them to "."
41+toNormalizedFilePath' "" = emptyFilePath
42+toNormalizedFilePath' fp = LSP.toNormalizedFilePath fp
43+
44+emptyFilePath :: LSP.NormalizedFilePath
45+emptyFilePath = LSP.NormalizedFilePath emptyPathUri ""
46+
47+-- | We use an empty string as a filepath when we don’t have a file.
48+-- However, haskell-lsp doesn’t support that in uriToFilePath and given
49+-- that it is not a valid filepath it does not make sense to upstream a fix.
50+-- So we have our own wrapper here that supports empty filepaths.
51+uriToFilePath' :: LSP.Uri -> Maybe FilePath
52+uriToFilePath' uri
53+ | uri == LSP.fromNormalizedUri emptyPathUri = Just ""
54+ | otherwise = LSP.uriToFilePath uri
55+
56+emptyPathUri :: LSP.NormalizedUri
57+emptyPathUri =
58+ let s = "file://"
59+ in LSP.NormalizedUri (hash s) s
60+
61+filePathToUri' :: LSP.NormalizedFilePath -> LSP.NormalizedUri
62+filePathToUri' = LSP.normalizedFilePathToUri
63+
64+fromUri :: LSP.NormalizedUri -> LSP.NormalizedFilePath
65+fromUri = fromMaybe (toNormalizedFilePath' noFilePath) . LSP.uriToNormalizedFilePath
3066
3167noFilePath :: FilePath
3268noFilePath = "<unknown>"
@@ -35,44 +71,42 @@ noFilePath = "<unknown>"
3571noRange :: Range
3672noRange = Range (Position 0 0) (Position 100000 0)
3773
38−
39−-- | A dummy location to use when location information is not present because
40−-- the code was generated.
41−genLocation :: Location
42−genLocation = Location
43− { _uri = Uri "<generated>"
44− , _range = Range (Position 0 0) (Position 0 0)
45− }
46−
47−
48−-- | Is a location generated.
49−isGenLocation :: Location -> Bool
50−isGenLocation x = _uri x == Uri "<generated>"
51−
52−
53−-- | Check if a position is inside a range.
54−-- Our definition states that the start of the range is included, but not the end.
55−inRange :: Position -> Range -> Bool
56−inRange pos (Range start end) = start <= pos && pos < end
57−
58−
59−-- | Check if a position is inside a range, including the end.
60−-- Both start and end of the range are included.
61−inRangeClosed :: Position -> Range -> Bool
62−inRangeClosed pos (Range start end) = start <= pos && pos <= end
63−
64−
65−-- | Produce a new range where the minimum position is the min of both,
66−-- and the maximum position is the max of both.
67−appendRange :: Range -> Range -> Range
68−appendRange r1 r2
69− = Range { _start = min (_start r1) (_start r2)
70− , _end = max (_end r1) (_end r2) }
71−
72−
73−-- | Produce a new location where the ranges are the appended and we choose
74−-- the file path of the second.
75−appendLocation :: Location -> Location -> Location
76−appendLocation l1 l2
77− = Location { _uri = _uri l2
78− , _range = appendRange (_range l1) (_range l2) }
74+showPosition :: Position -> String
75+showPosition 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.hsnew file+54
@@ -0,0 +1,54 @@
1+-- Copyright (c) 2019 The DAML Authors. All rights reserved.
2+-- SPDX-License-Identifier: Apache-2.0
3+
4+{-# LANGUAGE RankNTypes #-}
5+-- | This is a compatibility module that abstracts over the
6+-- concrete choice of logging framework so users can plug in whatever
7+-- framework they want to.
8+module Development.IDE.Types.Logger
9+ ( Priority(..)
10+ , Logger(..)
11+ , logError, logWarning, logInfo, logDebug, logTelemetry
12+ , noLogging
13+ ) where
14+
15+import qualified Data.Text as T
16+
17+
18+data Priority
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)
29+
30+
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+99−7
@@ -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.
22-- SPDX-License-Identifier: Apache-2.0
33
44{-# LANGUAGE RankNTypes #-}
@@ -6,27 +6,95 @@
66-- | Options
77module Development.IDE.Types.Options
88 ( IdeOptions(..)
9+ , IdePreprocessedSource(..)
10+ , IdeReportProgress(..)
11+ , IdeDefer(..)
12+ , clientSupportsProgress
913 , IdePkgLocationOptions(..)
14+ , defaultIdeOptions
15+ , IdeResult
1016 ) where
1117
1218import Development.Shake
19+import Development.IDE.GHC.Util
1320import GHC hiding (parseModule, typecheckModule)
1421import GhcPlugins as GHC hiding (fst3, (<>))
15−
22+import qualified Language.Haskell.LSP.Types.Capabilities as LSP
23+import qualified Data.Text as T
24+import Development.IDE.Types.Diagnostics
1625
1726data IdeOptions = IdeOptions
18− { optPreprocessor :: GHC.ParsedSource -> ([(GHC.SrcSpan, String)], GHC.ParsedSource)
19− , optGhcSession :: Action HscEnv
20− -- ^ Setup a GHC session using a given package state. If a `ParsedModule` is supplied,
21− -- the import path should be setup for that module.
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.
30+ , optGhcSession :: Action (FilePath -> Action (IdeResult HscEnvEq))
31+ -- ^ Setup a GHC session for a given file, e.g. @Foo.hs@.
32+ -- For the same 'ComponentOptions' from hie-bios, the resulting function will be applied once per file.
33+ -- It is desirable that many files get the same 'HscEnvEq', so that more IDE features work.
2234 , optPkgLocationOpts :: IdePkgLocationOptions
23− , optWriteIface :: Bool
35+ -- ^ How to locate source and @.hie@ files given a module name.
2436 , optExtensions :: [String]
37+ -- ^ File extensions to search for code, defaults to Haskell sources (including @.hs@)
2538
2639 , 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.
2744 , 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.
50+ , optLanguageSyntax :: String
51+ -- ^ the ```language to use
52+ , optNewColonConvention :: Bool
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.
2872 }
2973
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+
81+defaultIdeOptions :: Action (FilePath -> Action (IdeResult HscEnvEq)) -> IdeOptions
82+defaultIdeOptions session = IdeOptions
83+ {optPreprocessor = IdePreprocessedSource [] []
84+ ,optGhcSession = session
85+ ,optExtensions = ["hs", "lhs"]
86+ ,optPkgLocationOpts = defaultIdePkgLocationOptions
87+ ,optThreads = 0
88+ ,optShakeFiles = Nothing
89+ ,optShakeProfiling = Nothing
90+ ,optReportProgress = IdeReportProgress False
91+ ,optLanguageSyntax = "haskell"
92+ ,optNewColonConvention = False
93+ ,optKeywords = haskellKeywords
94+ ,optDefer = IdeDefer True
95+ ,optTesting = False
96+ }
97+
3098
3199-- | The set of options used to locate files belonging to external packages.
32100data IdePkgLocationOptions = IdePkgLocationOptions
@@ -38,3 +106,27 @@ data IdePkgLocationOptions = IdePkgLocationOptions
38106 -- used to lookup settings like importDirs. For DAML, we place them in the package DB.
39107 -- For cabal this could point somewhere in ~/.cabal/packages.
40108 }
109+
110+defaultIdePkgLocationOptions :: IdePkgLocationOptions
111+defaultIdePkgLocationOptions = IdePkgLocationOptions f f
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+ ]
src/Development/IDE/UtilGHC.hsdeleted−86
@@ -1,86 +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−{-# OPTIONS_GHC -Wno-missing-fields #-} -- to enable prettyPrint
5−{-# LANGUAGE CPP #-}
6−
7−-- | GHC utility functions. Importantly, code using our GHC should never:
8−--
9−-- * Call runGhc, use runGhcFast instead. It's faster and doesn't require config we don't have.
10−--
11−-- * Call setSessionDynFlags, use modifyDynFlags instead. It's faster and avoids loading packages.
12−module Development.IDE.UtilGHC(
13− lookupPackageConfig,
14− modifyDynFlags,
15− fakeDynFlags,
16− prettyPrint,
17− runGhcEnv
18− ) where
19−
20−import Config
21−import Fingerprint
22−import GHC
23−import GhcMonad
24−import GhcPlugins
25−import Platform
26−import Data.IORef
27−import Control.Exception
28−import FileCleanup
29−
30−----------------------------------------------------------------------
31−-- GHC setup
32−
33−modifyDynFlags :: GhcMonad m => (DynFlags -> DynFlags) -> m ()
34−modifyDynFlags f = do
35− newFlags <- f <$> getSessionDynFlags
36− -- We do not use setSessionDynFlags here since we handle package
37− -- initialization separately.
38− modifySession $ \h ->
39− h { hsc_dflags = newFlags, hsc_IC = (hsc_IC h) {ic_dflags = newFlags} }
40−
41−lookupPackageConfig :: UnitId -> HscEnv -> Maybe PackageConfig
42−lookupPackageConfig unitId env =
43− lookupPackage' False pkgConfigMap unitId
44− where
45− pkgConfigMap =
46− -- For some weird reason, the GHC API does not provide a way to get the PackageConfigMap
47− -- from PackageState so we have to wrap it in DynFlags first.
48− getPackageConfigMap $ hsc_dflags env
49−
50−
51−
52−prettyPrint :: Outputable a => a -> String
53−prettyPrint = showSDoc fakeDynFlags . ppr
54−
55−runGhcEnv :: HscEnv -> Ghc a -> IO a
56−runGhcEnv env act = do
57− filesToClean <- newIORef emptyFilesToClean
58− dirsToClean <- newIORef mempty
59− let dflags = (hsc_dflags env){filesToClean=filesToClean, dirsToClean=dirsToClean}
60− ref <- newIORef env{hsc_dflags=dflags}
61− unGhc act (Session ref) `finally` do
62− cleanTempFiles dflags
63− cleanTempDirs dflags
64−
65−
66−-- Fake DynFlags which are mostly undefined, but define enough to do a little bit
67−fakeDynFlags :: DynFlags
68−fakeDynFlags = defaultDynFlags settings ([], [])
69− where
70− settings = Settings
71− {sTargetPlatform = Platform
72− {platformWordSize = 8
73− ,platformOS = OSUnknown
74− ,platformUnregisterised = True
75− }
76− ,sPlatformConstants = PlatformConstants
77− {pc_DYNAMIC_BY_DEFAULT = False
78− ,pc_WORD_SIZE = 8
79− }
80−#ifndef GHC_STABLE
81− ,sIntegerLibraryType = IntegerSimple
82−#endif
83− ,sProjectVersion = cProjectVersion
84− ,sProgramName = "ghc"
85− ,sOpt_P_fingerprint = fingerprint0
86− }
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+17−1
@@ -1,3 +1,19 @@
1−resolver: nightly-2019-05-20
1+resolver: nightly-2019-09-21
22packages:
33- .
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+- 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
18+nix:
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/Demo.hsdeleted−104
@@ -1,104 +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−module Demo(main) where
5−
6−import Control.Concurrent.Extra
7−import Control.Monad
8−import System.Time.Extra
9−import Development.IDE.State.Service
10−import Development.IDE.State.Rules
11−import Development.IDE.State.Shake
12−import Development.IDE.State.RuleTypes
13−import Development.IDE.Types.Diagnostics
14−import Development.IDE.Types.Options
15−import Development.IDE.Logger
16−import qualified Data.Text.IO as T
17−import System.Environment
18−import Data.List
19−import Data.Maybe
20−import System.FilePath
21−import Data.Tuple.Extra
22−import System.IO.Extra
23−import Development.IDE.Types.LSP
24−import Development.Shake hiding (Env)
25−import qualified Data.Set as Set
26−
27−import CmdLineParser
28−import DynFlags
29−import Panic
30−import GHC
31−import GHC.Paths
32−
33−
34−main :: IO ()
35−main = do
36− (ghcOptions, files) <- getCmdLine
37−
38− -- lock to avoid overlapping output on stdout
39− lock <- newLock
40−
41− ide <- initialise
42− mainRule
43− (Just $ showEvent lock)
44− (makeOneHandle $ withLock lock . T.putStrLn)
45− IdeOptions
46− {optPreprocessor = (,) []
47− ,optWriteIface = False
48− ,optGhcSession = liftIO $ newSession ghcOptions
49− ,optExtensions = ["hs"]
50− ,optPkgLocationOpts = error "optPkgLocationOpts not implemented yet"
51− ,optThreads = 0
52− ,optShakeProfiling = Nothing -- Just "output.html"
53− }
54− setFilesOfInterest ide $ Set.fromList files
55− _ <- runAction ide $ uses_ TypeCheck files
56− -- shake now writes an async message that it is completed with timing info,
57− -- so we sleep briefly to wait for it to have been written
58− sleep 0.01
59− putStrLn "Done"
60−
61−
62−-- | Print an LSP event.
63−showEvent :: Lock -> Event -> IO ()
64−showEvent _ (EventFileDiagnostics (_, [])) = return ()
65−showEvent lock (EventFileDiagnostics (file, diags)) =
66− withLock lock $ T.putStrLn $ showDiagnosticsColored $ map (file,) diags
67−showEvent lock e = withLock lock $ print e
68−
69−
70−-- | Create a GHC session that will be subsequently reused.
71−newSession :: [String] -> IO HscEnv
72−newSession flags = runGhc (Just libdir) $ do
73− damlDFlags <- getSessionDynFlags
74− (dflags', leftover, warns) <- parseDynamicFlagsCmdLine damlDFlags $ map noLoc flags
75−
76− let leftoverError = CmdLineError $
77− (unlines . ("Unable to parse custom flags:":) . map unLoc) leftover
78− unless (null leftover) $ liftIO $ throwGhcExceptionIO leftoverError
79−
80− unless (null warns) $
81− liftIO $ putStrLn $ unlines $ "Warnings:" : map (unLoc . warnMsg) warns
82−
83− _ <- setSessionDynFlags dflags'
84− getSession
85−
86−
87−-- | Convert the command line into GHC options and files to load.
88−getCmdLine :: IO ([String], [FilePath])
89−getCmdLine = do
90− args <- getArgs
91− args <- return $ if null args then [".ghci"] else args
92− let (flags, files) = partition ("-" `isPrefixOf`) args
93− let (ghci, hs) = partition ((==) ".ghci" . takeExtension) files
94− (flags, files) <- both concat . unzip . ((flags,hs):) <$> mapM readGhci ghci
95− when (null files) $
96− fail "Expected some files to load, but didn't find any"
97− return (flags, files)
98−
99−readGhci :: FilePath -> IO ([String], [FilePath])
100−readGhci file = do
101− xs <- lines <$> readFileUTF8' file
102− let flags = concatMap words $ mapMaybe (stripPrefix ":set ") xs
103− let files = concatMap words $ mapMaybe (stripPrefix ":load ") xs
104− return (flags, files)
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.