packages feed

hls-eval-plugin 1.0.0.0 → 1.1.0.0

raw patch · 113 files changed

+4050/−2632 lines, 113 filesdep +hls-eval-plugindep +hls-test-utilsdep +lsp-testdep ~Diffdep ~basedep ~ghcidenew-uploaderPVP ok

version bump matches the API change (PVP)

Dependencies added: hls-eval-plugin, hls-test-utils, lsp-test

Dependency ranges changed: Diff, base, ghcide, hls-plugin-api, megaparsec

API changes (from Hackage documentation)

+ Ide.Plugin.Eval.Types: Comments :: Map Range RawLineComment -> Map Range RawBlockComment -> Comments
+ Ide.Plugin.Eval.Types: EvalParams :: [Section] -> !TextDocumentIdentifier -> !EvalId -> EvalParams
+ Ide.Plugin.Eval.Types: Example :: NonEmpty Txt -> [Txt] -> Range -> Test
+ Ide.Plugin.Eval.Types: Haddock :: Language
+ Ide.Plugin.Eval.Types: Located :: l -> a -> Located l a
+ Ide.Plugin.Eval.Types: MultiLine :: Range -> Format
+ Ide.Plugin.Eval.Types: Plain :: Language
+ Ide.Plugin.Eval.Types: Property :: Txt -> [Txt] -> Range -> Test
+ Ide.Plugin.Eval.Types: RawBlockComment :: String -> RawBlockComment
+ Ide.Plugin.Eval.Types: RawLineComment :: String -> RawLineComment
+ Ide.Plugin.Eval.Types: Section :: Txt -> [Test] -> Language -> Format -> Section
+ Ide.Plugin.Eval.Types: Sections :: [Section] -> [Section] -> Sections
+ Ide.Plugin.Eval.Types: SingleLine :: Format
+ Ide.Plugin.Eval.Types: [blockComments] :: Comments -> Map Range RawBlockComment
+ Ide.Plugin.Eval.Types: [evalId] :: EvalParams -> !EvalId
+ Ide.Plugin.Eval.Types: [getRawBlockComment] :: RawBlockComment -> String
+ Ide.Plugin.Eval.Types: [getRawLineComment] :: RawLineComment -> String
+ Ide.Plugin.Eval.Types: [lineComments] :: Comments -> Map Range RawLineComment
+ Ide.Plugin.Eval.Types: [located] :: Located l a -> a
+ Ide.Plugin.Eval.Types: [location] :: Located l a -> l
+ Ide.Plugin.Eval.Types: [module_] :: EvalParams -> !TextDocumentIdentifier
+ Ide.Plugin.Eval.Types: [nonSetupSections] :: Sections -> [Section]
+ Ide.Plugin.Eval.Types: [sectionFormat] :: Section -> Format
+ Ide.Plugin.Eval.Types: [sectionLanguage] :: Section -> Language
+ Ide.Plugin.Eval.Types: [sectionName] :: Section -> Txt
+ Ide.Plugin.Eval.Types: [sectionTests] :: Section -> [Test]
+ Ide.Plugin.Eval.Types: [sections] :: EvalParams -> [Section]
+ Ide.Plugin.Eval.Types: [setupSections] :: Sections -> [Section]
+ Ide.Plugin.Eval.Types: [testLines] :: Test -> NonEmpty Txt
+ Ide.Plugin.Eval.Types: [testOutput] :: Test -> [Txt]
+ Ide.Plugin.Eval.Types: [testRange] :: Test -> Range
+ Ide.Plugin.Eval.Types: [testline] :: Test -> Txt
+ Ide.Plugin.Eval.Types: data Comments
+ Ide.Plugin.Eval.Types: data EvalParams
+ Ide.Plugin.Eval.Types: data Format
+ Ide.Plugin.Eval.Types: data Language
+ Ide.Plugin.Eval.Types: data Located l a
+ Ide.Plugin.Eval.Types: data Section
+ Ide.Plugin.Eval.Types: data Sections
+ Ide.Plugin.Eval.Types: data Test
+ Ide.Plugin.Eval.Types: hasPropertyTest :: Section -> Bool
+ Ide.Plugin.Eval.Types: hasTests :: Section -> Bool
+ Ide.Plugin.Eval.Types: instance (Control.DeepSeq.NFData l, Control.DeepSeq.NFData a) => Control.DeepSeq.NFData (Ide.Plugin.Eval.Types.Located l a)
+ Ide.Plugin.Eval.Types: instance (Data.Aeson.Types.FromJSON.FromJSON l, Data.Aeson.Types.FromJSON.FromJSON a) => Data.Aeson.Types.FromJSON.FromJSON (Ide.Plugin.Eval.Types.Located l a)
+ Ide.Plugin.Eval.Types: instance (Data.Aeson.Types.ToJSON.ToJSON a, Data.Aeson.Types.ToJSON.ToJSON l) => Data.Aeson.Types.ToJSON.ToJSON (Ide.Plugin.Eval.Types.Located l a)
+ Ide.Plugin.Eval.Types: instance (GHC.Classes.Eq l, GHC.Classes.Eq a) => GHC.Classes.Eq (Ide.Plugin.Eval.Types.Located l a)
+ Ide.Plugin.Eval.Types: instance (GHC.Classes.Ord l, GHC.Classes.Ord a) => GHC.Classes.Ord (Ide.Plugin.Eval.Types.Located l a)
+ Ide.Plugin.Eval.Types: instance (GHC.Show.Show l, GHC.Show.Show a) => GHC.Show.Show (Ide.Plugin.Eval.Types.Located l a)
+ Ide.Plugin.Eval.Types: instance Control.DeepSeq.NFData Ide.Plugin.Eval.Types.ExpectedLine
+ Ide.Plugin.Eval.Types: instance Control.DeepSeq.NFData Ide.Plugin.Eval.Types.Format
+ Ide.Plugin.Eval.Types: instance Control.DeepSeq.NFData Ide.Plugin.Eval.Types.Language
+ Ide.Plugin.Eval.Types: instance Control.DeepSeq.NFData Ide.Plugin.Eval.Types.LineChunk
+ Ide.Plugin.Eval.Types: instance Control.DeepSeq.NFData Ide.Plugin.Eval.Types.Section
+ Ide.Plugin.Eval.Types: instance Control.DeepSeq.NFData Ide.Plugin.Eval.Types.Test
+ Ide.Plugin.Eval.Types: instance Data.Aeson.Types.FromJSON.FromJSON Ide.Plugin.Eval.Types.EvalParams
+ Ide.Plugin.Eval.Types: instance Data.Aeson.Types.FromJSON.FromJSON Ide.Plugin.Eval.Types.ExpectedLine
+ Ide.Plugin.Eval.Types: instance Data.Aeson.Types.FromJSON.FromJSON Ide.Plugin.Eval.Types.Format
+ Ide.Plugin.Eval.Types: instance Data.Aeson.Types.FromJSON.FromJSON Ide.Plugin.Eval.Types.Language
+ Ide.Plugin.Eval.Types: instance Data.Aeson.Types.FromJSON.FromJSON Ide.Plugin.Eval.Types.LineChunk
+ Ide.Plugin.Eval.Types: instance Data.Aeson.Types.FromJSON.FromJSON Ide.Plugin.Eval.Types.Section
+ Ide.Plugin.Eval.Types: instance Data.Aeson.Types.FromJSON.FromJSON Ide.Plugin.Eval.Types.Test
+ Ide.Plugin.Eval.Types: instance Data.Aeson.Types.ToJSON.ToJSON Ide.Plugin.Eval.Types.EvalParams
+ Ide.Plugin.Eval.Types: instance Data.Aeson.Types.ToJSON.ToJSON Ide.Plugin.Eval.Types.ExpectedLine
+ Ide.Plugin.Eval.Types: instance Data.Aeson.Types.ToJSON.ToJSON Ide.Plugin.Eval.Types.Format
+ Ide.Plugin.Eval.Types: instance Data.Aeson.Types.ToJSON.ToJSON Ide.Plugin.Eval.Types.Language
+ Ide.Plugin.Eval.Types: instance Data.Aeson.Types.ToJSON.ToJSON Ide.Plugin.Eval.Types.LineChunk
+ Ide.Plugin.Eval.Types: instance Data.Aeson.Types.ToJSON.ToJSON Ide.Plugin.Eval.Types.Section
+ Ide.Plugin.Eval.Types: instance Data.Aeson.Types.ToJSON.ToJSON Ide.Plugin.Eval.Types.Test
+ Ide.Plugin.Eval.Types: instance Data.String.IsString Ide.Plugin.Eval.Types.ExpectedLine
+ Ide.Plugin.Eval.Types: instance Data.String.IsString Ide.Plugin.Eval.Types.LineChunk
+ Ide.Plugin.Eval.Types: instance Data.String.IsString Ide.Plugin.Eval.Types.RawBlockComment
+ Ide.Plugin.Eval.Types: instance Data.String.IsString Ide.Plugin.Eval.Types.RawLineComment
+ Ide.Plugin.Eval.Types: instance GHC.Base.Functor (Ide.Plugin.Eval.Types.Located l)
+ Ide.Plugin.Eval.Types: instance GHC.Base.Monoid Ide.Plugin.Eval.Types.Comments
+ Ide.Plugin.Eval.Types: instance GHC.Base.Monoid Ide.Plugin.Eval.Types.RawBlockComment
+ Ide.Plugin.Eval.Types: instance GHC.Base.Monoid Ide.Plugin.Eval.Types.RawLineComment
+ Ide.Plugin.Eval.Types: instance GHC.Base.Semigroup Ide.Plugin.Eval.Types.Comments
+ Ide.Plugin.Eval.Types: instance GHC.Base.Semigroup Ide.Plugin.Eval.Types.RawBlockComment
+ Ide.Plugin.Eval.Types: instance GHC.Base.Semigroup Ide.Plugin.Eval.Types.RawLineComment
+ Ide.Plugin.Eval.Types: instance GHC.Classes.Eq Ide.Plugin.Eval.Types.Comments
+ Ide.Plugin.Eval.Types: instance GHC.Classes.Eq Ide.Plugin.Eval.Types.EvalParams
+ Ide.Plugin.Eval.Types: instance GHC.Classes.Eq Ide.Plugin.Eval.Types.ExpectedLine
+ Ide.Plugin.Eval.Types: instance GHC.Classes.Eq Ide.Plugin.Eval.Types.Format
+ Ide.Plugin.Eval.Types: instance GHC.Classes.Eq Ide.Plugin.Eval.Types.Language
+ Ide.Plugin.Eval.Types: instance GHC.Classes.Eq Ide.Plugin.Eval.Types.LineChunk
+ Ide.Plugin.Eval.Types: instance GHC.Classes.Eq Ide.Plugin.Eval.Types.RawBlockComment
+ Ide.Plugin.Eval.Types: instance GHC.Classes.Eq Ide.Plugin.Eval.Types.RawLineComment
+ Ide.Plugin.Eval.Types: instance GHC.Classes.Eq Ide.Plugin.Eval.Types.Section
+ Ide.Plugin.Eval.Types: instance GHC.Classes.Eq Ide.Plugin.Eval.Types.Sections
+ Ide.Plugin.Eval.Types: instance GHC.Classes.Eq Ide.Plugin.Eval.Types.Test
+ Ide.Plugin.Eval.Types: instance GHC.Classes.Ord Ide.Plugin.Eval.Types.Comments
+ Ide.Plugin.Eval.Types: instance GHC.Classes.Ord Ide.Plugin.Eval.Types.Format
+ Ide.Plugin.Eval.Types: instance GHC.Classes.Ord Ide.Plugin.Eval.Types.Language
+ Ide.Plugin.Eval.Types: instance GHC.Classes.Ord Ide.Plugin.Eval.Types.RawBlockComment
+ Ide.Plugin.Eval.Types: instance GHC.Classes.Ord Ide.Plugin.Eval.Types.RawLineComment
+ Ide.Plugin.Eval.Types: instance GHC.Generics.Generic (Ide.Plugin.Eval.Types.Located l a)
+ Ide.Plugin.Eval.Types: instance GHC.Generics.Generic Ide.Plugin.Eval.Types.Comments
+ Ide.Plugin.Eval.Types: instance GHC.Generics.Generic Ide.Plugin.Eval.Types.EvalParams
+ Ide.Plugin.Eval.Types: instance GHC.Generics.Generic Ide.Plugin.Eval.Types.ExpectedLine
+ Ide.Plugin.Eval.Types: instance GHC.Generics.Generic Ide.Plugin.Eval.Types.Format
+ Ide.Plugin.Eval.Types: instance GHC.Generics.Generic Ide.Plugin.Eval.Types.Language
+ Ide.Plugin.Eval.Types: instance GHC.Generics.Generic Ide.Plugin.Eval.Types.LineChunk
+ Ide.Plugin.Eval.Types: instance GHC.Generics.Generic Ide.Plugin.Eval.Types.Section
+ Ide.Plugin.Eval.Types: instance GHC.Generics.Generic Ide.Plugin.Eval.Types.Sections
+ Ide.Plugin.Eval.Types: instance GHC.Generics.Generic Ide.Plugin.Eval.Types.Test
+ Ide.Plugin.Eval.Types: instance GHC.Show.Show Ide.Plugin.Eval.Types.Comments
+ Ide.Plugin.Eval.Types: instance GHC.Show.Show Ide.Plugin.Eval.Types.EvalParams
+ Ide.Plugin.Eval.Types: instance GHC.Show.Show Ide.Plugin.Eval.Types.ExpectedLine
+ Ide.Plugin.Eval.Types: instance GHC.Show.Show Ide.Plugin.Eval.Types.Format
+ Ide.Plugin.Eval.Types: instance GHC.Show.Show Ide.Plugin.Eval.Types.Language
+ Ide.Plugin.Eval.Types: instance GHC.Show.Show Ide.Plugin.Eval.Types.LineChunk
+ Ide.Plugin.Eval.Types: instance GHC.Show.Show Ide.Plugin.Eval.Types.RawBlockComment
+ Ide.Plugin.Eval.Types: instance GHC.Show.Show Ide.Plugin.Eval.Types.RawLineComment
+ Ide.Plugin.Eval.Types: instance GHC.Show.Show Ide.Plugin.Eval.Types.Section
+ Ide.Plugin.Eval.Types: instance GHC.Show.Show Ide.Plugin.Eval.Types.Sections
+ Ide.Plugin.Eval.Types: instance GHC.Show.Show Ide.Plugin.Eval.Types.Test
+ Ide.Plugin.Eval.Types: instance Text.Megaparsec.Stream.Stream Ide.Plugin.Eval.Types.RawBlockComment
+ Ide.Plugin.Eval.Types: instance Text.Megaparsec.Stream.Stream Ide.Plugin.Eval.Types.RawLineComment
+ Ide.Plugin.Eval.Types: instance Text.Megaparsec.Stream.TraversableStream Ide.Plugin.Eval.Types.RawBlockComment
+ Ide.Plugin.Eval.Types: instance Text.Megaparsec.Stream.TraversableStream Ide.Plugin.Eval.Types.RawLineComment
+ Ide.Plugin.Eval.Types: instance Text.Megaparsec.Stream.VisualStream Ide.Plugin.Eval.Types.RawBlockComment
+ Ide.Plugin.Eval.Types: instance Text.Megaparsec.Stream.VisualStream Ide.Plugin.Eval.Types.RawLineComment
+ Ide.Plugin.Eval.Types: isProperty :: Test -> Bool
+ Ide.Plugin.Eval.Types: locate :: Loc [a] -> [Loc a]
+ Ide.Plugin.Eval.Types: locate0 :: [a] -> [Loc a]
+ Ide.Plugin.Eval.Types: newtype RawBlockComment
+ Ide.Plugin.Eval.Types: newtype RawLineComment
+ Ide.Plugin.Eval.Types: splitSections :: [Section] -> ([Section], [Section])
+ Ide.Plugin.Eval.Types: type Loc = Located Line
+ Ide.Plugin.Eval.Types: type Txt = String
+ Ide.Plugin.Eval.Types: unLoc :: Located l a -> a

Files

LICENSE view
@@ -1,201 +1,201 @@-                                 Apache License-                           Version 2.0, January 2004-                        http://www.apache.org/licenses/--   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION--   1. Definitions.--      "License" shall mean the terms and conditions for use, reproduction,-      and distribution as defined by Sections 1 through 9 of this document.--      "Licensor" shall mean the copyright owner or entity authorized by-      the copyright owner that is granting the License.--      "Legal Entity" shall mean the union of the acting entity and all-      other entities that control, are controlled by, or are under common-      control with that entity. For the purposes of this definition,-      "control" means (i) the power, direct or indirect, to cause the-      direction or management of such entity, whether by contract or-      otherwise, or (ii) ownership of fifty percent (50%) or more of the-      outstanding shares, or (iii) beneficial ownership of such entity.--      "You" (or "Your") shall mean an individual or Legal Entity-      exercising permissions granted by this License.--      "Source" form shall mean the preferred form for making modifications,-      including but not limited to software source code, documentation-      source, and configuration files.--      "Object" form shall mean any form resulting from mechanical-      transformation or translation of a Source form, including but-      not limited to compiled object code, generated documentation,-      and conversions to other media types.--      "Work" shall mean the work of authorship, whether in Source or-      Object form, made available under the License, as indicated by a-      copyright notice that is included in or attached to the work-      (an example is provided in the Appendix below).--      "Derivative Works" shall mean any work, whether in Source or Object-      form, that is based on (or derived from) the Work and for which the-      editorial revisions, annotations, elaborations, or other modifications-      represent, as a whole, an original work of authorship. For the purposes-      of this License, Derivative Works shall not include works that remain-      separable from, or merely link (or bind by name) to the interfaces of,-      the Work and Derivative Works thereof.--      "Contribution" shall mean any work of authorship, including-      the original version of the Work and any modifications or additions-      to that Work or Derivative Works thereof, that is intentionally-      submitted to Licensor for inclusion in the Work by the copyright owner-      or by an individual or Legal Entity authorized to submit on behalf of-      the copyright owner. For the purposes of this definition, "submitted"-      means any form of electronic, verbal, or written communication sent-      to the Licensor or its representatives, including but not limited to-      communication on electronic mailing lists, source code control systems,-      and issue tracking systems that are managed by, or on behalf of, the-      Licensor for the purpose of discussing and improving the Work, but-      excluding communication that is conspicuously marked or otherwise-      designated in writing by the copyright owner as "Not a Contribution."--      "Contributor" shall mean Licensor and any individual or Legal Entity-      on behalf of whom a Contribution has been received by Licensor and-      subsequently incorporated within the Work.--   2. Grant of Copyright License. Subject to the terms and conditions of-      this License, each Contributor hereby grants to You a perpetual,-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable-      copyright license to reproduce, prepare Derivative Works of,-      publicly display, publicly perform, sublicense, and distribute the-      Work and such Derivative Works in Source or Object form.--   3. Grant of Patent License. Subject to the terms and conditions of-      this License, each Contributor hereby grants to You a perpetual,-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable-      (except as stated in this section) patent license to make, have made,-      use, offer to sell, sell, import, and otherwise transfer the Work,-      where such license applies only to those patent claims licensable-      by such Contributor that are necessarily infringed by their-      Contribution(s) alone or by combination of their Contribution(s)-      with the Work to which such Contribution(s) was submitted. If You-      institute patent litigation against any entity (including a-      cross-claim or counterclaim in a lawsuit) alleging that the Work-      or a Contribution incorporated within the Work constitutes direct-      or contributory patent infringement, then any patent licenses-      granted to You under this License for that Work shall terminate-      as of the date such litigation is filed.--   4. Redistribution. You may reproduce and distribute copies of the-      Work or Derivative Works thereof in any medium, with or without-      modifications, and in Source or Object form, provided that You-      meet the following conditions:--      (a) You must give any other recipients of the Work or-          Derivative Works a copy of this License; and--      (b) You must cause any modified files to carry prominent notices-          stating that You changed the files; and--      (c) You must retain, in the Source form of any Derivative Works-          that You distribute, all copyright, patent, trademark, and-          attribution notices from the Source form of the Work,-          excluding those notices that do not pertain to any part of-          the Derivative Works; and--      (d) If the Work includes a "NOTICE" text file as part of its-          distribution, then any Derivative Works that You distribute must-          include a readable copy of the attribution notices contained-          within such NOTICE file, excluding those notices that do not-          pertain to any part of the Derivative Works, in at least one-          of the following places: within a NOTICE text file distributed-          as part of the Derivative Works; within the Source form or-          documentation, if provided along with the Derivative Works; or,-          within a display generated by the Derivative Works, if and-          wherever such third-party notices normally appear. The contents-          of the NOTICE file are for informational purposes only and-          do not modify the License. You may add Your own attribution-          notices within Derivative Works that You distribute, alongside-          or as an addendum to the NOTICE text from the Work, provided-          that such additional attribution notices cannot be construed-          as modifying the License.--      You may add Your own copyright statement to Your modifications and-      may provide additional or different license terms and conditions-      for use, reproduction, or distribution of Your modifications, or-      for any such Derivative Works as a whole, provided Your use,-      reproduction, and distribution of the Work otherwise complies with-      the conditions stated in this License.--   5. Submission of Contributions. Unless You explicitly state otherwise,-      any Contribution intentionally submitted for inclusion in the Work-      by You to the Licensor shall be under the terms and conditions of-      this License, without any additional terms or conditions.-      Notwithstanding the above, nothing herein shall supersede or modify-      the terms of any separate license agreement you may have executed-      with Licensor regarding such Contributions.--   6. Trademarks. This License does not grant permission to use the trade-      names, trademarks, service marks, or product names of the Licensor,-      except as required for reasonable and customary use in describing the-      origin of the Work and reproducing the content of the NOTICE file.--   7. Disclaimer of Warranty. Unless required by applicable law or-      agreed to in writing, Licensor provides the Work (and each-      Contributor provides its Contributions) on an "AS IS" BASIS,-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or-      implied, including, without limitation, any warranties or conditions-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A-      PARTICULAR PURPOSE. You are solely responsible for determining the-      appropriateness of using or redistributing the Work and assume any-      risks associated with Your exercise of permissions under this License.--   8. Limitation of Liability. In no event and under no legal theory,-      whether in tort (including negligence), contract, or otherwise,-      unless required by applicable law (such as deliberate and grossly-      negligent acts) or agreed to in writing, shall any Contributor be-      liable to You for damages, including any direct, indirect, special,-      incidental, or consequential damages of any character arising as a-      result of this License or out of the use or inability to use the-      Work (including but not limited to damages for loss of goodwill,-      work stoppage, computer failure or malfunction, or any and all-      other commercial damages or losses), even if such Contributor-      has been advised of the possibility of such damages.--   9. Accepting Warranty or Additional Liability. While redistributing-      the Work or Derivative Works thereof, You may choose to offer,-      and charge a fee for, acceptance of support, warranty, indemnity,-      or other liability obligations and/or rights consistent with this-      License. However, in accepting such obligations, You may act only-      on Your own behalf and on Your sole responsibility, not on behalf-      of any other Contributor, and only if You agree to indemnify,-      defend, and hold each Contributor harmless for any liability-      incurred by, or claims asserted against, such Contributor by reason-      of your accepting any such warranty or additional liability.--   END OF TERMS AND CONDITIONS--   APPENDIX: How to apply the Apache License to your work.--      To apply the Apache License to your work, attach the following-      boilerplate notice, with the fields enclosed by brackets "[]"-      replaced with your own identifying information. (Don't include-      the brackets!)  The text should be enclosed in the appropriate-      comment syntax for the file format. We also recommend that a-      file or class name and description of purpose be included on the-      same "printed page" as the copyright notice for easier-      identification within third-party archives.--   Copyright [yyyy] [name of copyright owner]--   Licensed under the Apache License, Version 2.0 (the "License");-   you may not use this file except in compliance with the License.-   You may obtain a copy of the License at--       http://www.apache.org/licenses/LICENSE-2.0--   Unless required by applicable law or agreed to in writing, software-   distributed under the License is distributed on an "AS IS" BASIS,-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.-   See the License for the specific language governing permissions and-   limitations under the License.+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
README.md view
@@ -1,332 +1,332 @@-# Eval plugin for the [Haskell Language Server](https://github.com/haskell/haskell-language-server#readme)--The Eval plugin evaluates code inserted in comments.--This is mainly useful to test and document functions and to quickly evaluate small expressions.--Every line of code to be evaluated is introduced by __>>>__--A quick calculation:--```--- >>> 2**4.5/pi--- 7.202530529256849-```--A little test for the `double` function:--```-{- |-A doubling function.-->>> double 11-22--}-double = (2*)-```--# Demo--![Eval](demo.gif)--# Test Structure--A test is composed by a sequence of contiguous lines, the result of their evaluation is inserted after the test body:--```->>> "AB" ++ "CD"->>> "CD" ++ "AB"-"ABCD"-"CDAB"-```--You execute a test by clicking on the _Evaluate_ code lens that appears above it (or _Refresh_, if the test has been run previously).--All tests in the same comment block are executed together.---Tests can appear in all kind of comments:-* plain comments (both single and multi line)-```-{-->>> "ab" ++ "c"-"abc"--}---- >>> "ab" ++ "c"--- "abc"-```-* Haddock commands (both single and multi line, forward and backward)-```-{-->>> "ab" ++ "c"-"abc"--}---- >>> "ab" ++ "c"--- "abc"--double a = a + a--- ^ A doubling function--- >>> double 11--- 22-```--Modules that use CPP and Literate Haskell (Bird-style only) modules are also supported (for GHC >= 8.8).--# Test Components--In general, a test is a sequence of:-* imports-* directives-* statements-* expressions-* properties--in no particular order, with every line introduced by __>>>__ (or __prop>__ in the case of properties).--### Imports--```->>> import Data.List->>> import GHC.TypeNats-```--From any package in scope but currently NOT from modules in the same source directory.--### Language Extensions--```->>> :set -XScopedTypeVariables -XStandaloneDeriving -XDataKinds -XTypeOperators -XExplicitNamespaces-```--### Statements and Declarations--Function declarations (optionally introduced by __let__):--```->>> let tuple x = (x,x)->>> let one=1;two=2->>> triple x = (x,x,x)-```--Any other declaration:--```->>> data TertiumDatur = Truly | Falsely | Other deriving Show->>> class Display a where display :: a -> String->>> instance Display TertiumDatur where display = show-```--Definitions are available to following tests in the __same__ comment:--```-{-->>> two = 2-->>> two-2--}---- >>> two--- Variable not in scope: two-```--If you want definitions to be available to all tests in the module, define a setup section:--```--- $setup--- >>> eleven = 11--{--eleven is now available to any test:-->>> eleven*2-22--}-```---### Type and Kind directives--```->>> :type Truly-Truly :: TertiumDatur-->>> :kind TertiumDatur-TertiumDatur :: *-->>> :type 3-3 :: forall p. Num p => p-->>> :type +d 3-3 :: Integer-->>> type N = 1->>> type M = 40->>> :kind! N + M + 1-N + M + 1 :: Nat-= 42-```--### Expressions--```->>> tuple 2->>> triple 3->>> display Other-(2,2)-(3,3,3)-"Other"-```--IO expressions can also be evaluated but their output to stdout/stderr is NOT captured:--```->>> print "foo"-()-```--### Properties--```-prop> \(l::[Int]) -> reverse (reverse l) == l-+++ OK, passed 100 tests.-```--# Haddock vs Plain Comments--There is a conceptual difference between Haddock and plain comments:-* Haddock comments constitute the external module's documentation, they state the contract between the implementor and the module users (API)-* Plain comments are internal documentation meant to explain how the code works (implementation).--This conceptual difference is reflected in the way tests results are refreshed by the Eval plugin.--Say that we have defined a `double` function as:--```-double = (*2)-```--And, in an Haddock comment, we run the test:--```-{- |->>> double 11-22--}-```--We then change the definition to:--```-double = (*3)-```--When we refresh the test, its current result is compared with the previous one and differences are displayed (as they change the API):--```-{- |->>> double 11-WAS 22-NOW 33--}-```--On the contrary, if the test were into a plain comment, the result would simply be replaced:--```-{-->>> double 11-33--}-```--# Multiline Output--By default, the output of every expression is returned as a single line.--This is a problem if you want, for example, to pretty print a value (in this case using the [pretty-simple](https://hackage.haskell.org/package/pretty-simple) package):--```->>> import Text.Pretty.Simple->>> pShowNoColor [1..3]-"[ 1\n, 2\n, 3\n]"-```--We could try to print the pretty-print output, but stdout is not captured so we get just a ():--```->>> print $ pShowNoColor [1..7]-()-```--To display it properly, we can exploit the fact that the output of an error is displayed as a multi-line text:--```->>> import qualified Data.Text.Lazy as TL->>> import Text.Pretty.Simple->>> prettyPrint v = error (TL.unpack $ pShowNoColor v) :: IO String->>> prettyPrint [1..3]-[ 1-, 2-, 3-]-```--# Differences with doctest--Though the Eval plugin functionality is quite similar to that of [doctest](https://hackage.haskell.org/package/doctest), some doctest's features are not supported.--### Capturing Stdout--Only the value of an IO expression is spliced in, not its output:--```->>> print "foo"-()-```--### Pattern Matching--The arbitrary content matcher __...__ is unsupported.--### Missing lambda abstractions in property tests--Variables are not automatically introduced:--```-prop> reverse (reverse l) == (l::[Int])-Variable not in scope: l :: [Int]-```--This works:--```-prop> \(l::[Int]) -> reverse (reverse l) == l-+++ OK, passed 100 tests.-```--### Multiline Expressions--```- >>> :{-  let-    x = 1-    y = 2-  in x + y + multiline- :}-```--# Acknowledgments--Design/features derived from:--* [GHCi](https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/ghci.html)--* [Haddock's](https://www.haskell.org/haddock/doc/html/ch03s08.html#idm140354810775744) Examples and Properties--* [Doctest](https://hackage.haskell.org/package/doctest)--* the REPLoid feature of [Dante](https://github.com/jyp/dante)-+# Eval plugin for the [Haskell Language Server](https://github.com/haskell/haskell-language-server#readme)
+
+The Eval plugin evaluates code inserted in comments.
+
+This is mainly useful to test and document functions and to quickly evaluate small expressions.
+
+Every line of code to be evaluated is introduced by __>>>__
+
+A quick calculation:
+
+```
+-- >>> 2**4.5/pi
+-- 7.202530529256849
+```
+
+A little test for the `double` function:
+
+```
+{- |
+A doubling function.
+
+>>> double 11
+22
+-}
+double = (2*)
+```
+
+# Demo
+
+![Eval](demo.gif)
+
+# Test Structure
+
+A test is composed by a sequence of contiguous lines, the result of their evaluation is inserted after the test body:
+
+```
+>>> "AB" ++ "CD"
+>>> "CD" ++ "AB"
+"ABCD"
+"CDAB"
+```
+
+You execute a test by clicking on the _Evaluate_ code lens that appears above it (or _Refresh_, if the test has been run previously).
+
+All tests in the same comment block are executed together.
+
+
+Tests can appear in all kind of comments:
+* plain comments (both single and multi line)
+```
+{-
+>>> "ab" ++ "c"
+"abc"
+-}
+
+-- >>> "ab" ++ "c"
+-- "abc"
+```
+* Haddock commands (both single and multi line, forward and backward)
+```
+{-
+>>> "ab" ++ "c"
+"abc"
+-}
+
+-- >>> "ab" ++ "c"
+-- "abc"
+
+double a = a + a
+-- ^ A doubling function
+-- >>> double 11
+-- 22
+```
+
+Modules that use CPP and Literate Haskell (Bird-style only) modules are also supported (for GHC >= 8.8).
+
+# Test Components
+
+In general, a test is a sequence of:
+* imports
+* directives
+* statements
+* expressions
+* properties
+
+in no particular order, with every line introduced by __>>>__ (or __prop>__ in the case of properties).
+
+### Imports
+
+```
+>>> import Data.List
+>>> import GHC.TypeNats
+```
+
+From any package in scope but currently NOT from modules in the same source directory.
+
+### Language Extensions
+
+```
+>>> :set -XScopedTypeVariables -XStandaloneDeriving -XDataKinds -XTypeOperators -XExplicitNamespaces
+```
+
+### Statements and Declarations
+
+Function declarations (optionally introduced by __let__):
+
+```
+>>> let tuple x = (x,x)
+>>> let one=1;two=2
+>>> triple x = (x,x,x)
+```
+
+Any other declaration:
+
+```
+>>> data TertiumDatur = Truly | Falsely | Other deriving Show
+>>> class Display a where display :: a -> String
+>>> instance Display TertiumDatur where display = show
+```
+
+Definitions are available to following tests in the __same__ comment:
+
+```
+{-
+>>> two = 2
+
+>>> two
+2
+-}
+
+-- >>> two
+-- Variable not in scope: two
+```
+
+If you want definitions to be available to all tests in the module, define a setup section:
+
+```
+-- $setup
+-- >>> eleven = 11
+
+{-
+eleven is now available to any test:
+
+>>> eleven*2
+22
+-}
+```
+
+
+### Type and Kind directives
+
+```
+>>> :type Truly
+Truly :: TertiumDatur
+
+>>> :kind TertiumDatur
+TertiumDatur :: *
+
+>>> :type 3
+3 :: forall p. Num p => p
+
+>>> :type +d 3
+3 :: Integer
+
+>>> type N = 1
+>>> type M = 40
+>>> :kind! N + M + 1
+N + M + 1 :: Nat
+= 42
+```
+
+### Expressions
+
+```
+>>> tuple 2
+>>> triple 3
+>>> display Other
+(2,2)
+(3,3,3)
+"Other"
+```
+
+IO expressions can also be evaluated but their output to stdout/stderr is NOT captured:
+
+```
+>>> print "foo"
+()
+```
+
+### Properties
+
+```
+prop> \(l::[Int]) -> reverse (reverse l) == l
++++ OK, passed 100 tests.
+```
+
+# Haddock vs Plain Comments
+
+There is a conceptual difference between Haddock and plain comments:
+* Haddock comments constitute the external module's documentation, they state the contract between the implementor and the module users (API)
+* Plain comments are internal documentation meant to explain how the code works (implementation).
+
+This conceptual difference is reflected in the way tests results are refreshed by the Eval plugin.
+
+Say that we have defined a `double` function as:
+
+```
+double = (*2)
+```
+
+And, in an Haddock comment, we run the test:
+
+```
+{- |
+>>> double 11
+22
+-}
+```
+
+We then change the definition to:
+
+```
+double = (*3)
+```
+
+When we refresh the test, its current result is compared with the previous one and differences are displayed (as they change the API):
+
+```
+{- |
+>>> double 11
+WAS 22
+NOW 33
+-}
+```
+
+On the contrary, if the test were into a plain comment, the result would simply be replaced:
+
+```
+{-
+>>> double 11
+33
+-}
+```
+
+# Multiline Output
+
+By default, the output of every expression is returned as a single line.
+
+This is a problem if you want, for example, to pretty print a value (in this case using the [pretty-simple](https://hackage.haskell.org/package/pretty-simple) package):
+
+```
+>>> import Text.Pretty.Simple
+>>> pShowNoColor [1..3]
+"[ 1\n, 2\n, 3\n]"
+```
+
+We could try to print the pretty-print output, but stdout is not captured so we get just a ():
+
+```
+>>> print $ pShowNoColor [1..7]
+()
+```
+
+To display it properly, we can exploit the fact that the output of an error is displayed as a multi-line text:
+
+```
+>>> import qualified Data.Text.Lazy as TL
+>>> import Text.Pretty.Simple
+>>> prettyPrint v = error (TL.unpack $ pShowNoColor v) :: IO String
+>>> prettyPrint [1..3]
+[ 1
+, 2
+, 3
+]
+```
+
+# Differences with doctest
+
+Though the Eval plugin functionality is quite similar to that of [doctest](https://hackage.haskell.org/package/doctest), some doctest's features are not supported.
+
+### Capturing Stdout
+
+Only the value of an IO expression is spliced in, not its output:
+
+```
+>>> print "foo"
+()
+```
+
+### Pattern Matching
+
+The arbitrary content matcher __...__ is unsupported.
+
+### Missing lambda abstractions in property tests
+
+Variables are not automatically introduced:
+
+```
+prop> reverse (reverse l) == (l::[Int])
+Variable not in scope: l :: [Int]
+```
+
+This works:
+
+```
+prop> \(l::[Int]) -> reverse (reverse l) == l
++++ OK, passed 100 tests.
+```
+
+### Multiline Expressions
+
+```
+ >>> :{
+  let
+    x = 1
+    y = 2
+  in x + y + multiline
+ :}
+```
+
+# Acknowledgments
+
+Design/features derived from:
+
+* [GHCi](https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/ghci.html)
+
+* [Haddock's](https://www.haskell.org/haddock/doc/html/ch03s08.html#idm140354810775744) Examples and Properties
+
+* [Doctest](https://hackage.haskell.org/package/doctest)
+
+* the REPLoid feature of [Dante](https://github.com/jyp/dante)
+
hls-eval-plugin.cabal view
@@ -1,78 +1,112 @@-cabal-version:      2.2-name:               hls-eval-plugin-version:            1.0.0.0-synopsis:           Eval plugin for Haskell Language Server-description:        Please see the README on GitHub at <https://github.com/haskell/haskell-language-server#readme>-category:           Development-bug-reports:        https://github.com/haskell/haskell-language-server/issues-license:            Apache-2.0-license-file:       LICENSE-author:             https://github.com/haskell/haskell-language-server/contributors-maintainer:         https://github.com/haskell/haskell-language-server/contributors--build-type:         Simple-extra-source-files:-  LICENSE-  README.md--flag pedantic-  description: Enable -Werror-  default:     False-  manual:      True--source-repository head-  type:     git-  location: https://github.com/haskell/haskell-language-server--library-  exposed-modules:  Ide.Plugin.Eval-  hs-source-dirs:   src-  other-modules:-    Ide.Plugin.Eval.Code-    Ide.Plugin.Eval.CodeLens-    Ide.Plugin.Eval.GHC-    Ide.Plugin.Eval.Parse.Comments-    Ide.Plugin.Eval.Parse.Option-    Ide.Plugin.Eval.Types-    Ide.Plugin.Eval.Util--  build-depends:-    , aeson-    , base                  >=4.12    && <5-    , containers-    , deepseq-    , Diff-    , directory-    , dlist-    , extra-    , filepath-    , ghc-    , ghc-boot-th-    , ghc-paths-    , ghcide                ^>= 1.0.0.0-    , hashable-    , lsp-    , lsp-types-    , hls-plugin-api        ^>= 1.0.0.0-    , lens-    , megaparsec            >=0.9-    , mtl-    , parser-combinators-    , pretty-simple-    , QuickCheck-    , safe-exceptions-    , shake-    , temporary-    , text-    , time-    , transformers-    , unordered-containers-    , unliftio--  ghc-options:      -Wall -Wno-name-shadowing -Wno-unticked-promoted-constructors--  if flag(pedantic)-    ghc-options: -Werror--  default-language: Haskell2010-  default-extensions: DataKinds, TypeOperators+cabal-version:      2.4
+name:               hls-eval-plugin
+version:            1.1.0.0
+synopsis:           Eval plugin for Haskell Language Server
+description:
+  Please see the README on GitHub at <https://github.com/haskell/haskell-language-server#readme>
+
+category:           Development
+bug-reports:        https://github.com/haskell/haskell-language-server/issues
+license:            Apache-2.0
+license-file:       LICENSE
+author:
+  https://github.com/haskell/haskell-language-server/contributors
+
+maintainer:
+  https://github.com/haskell/haskell-language-server/contributors
+
+build-type:         Simple
+extra-source-files:
+  LICENSE
+  README.md
+  test/testdata/*.cabal
+  test/testdata/*.expected
+  test/testdata/*.hs
+  test/testdata/*.lhs
+  test/testdata/*.yaml
+
+flag pedantic
+  description: Enable -Werror
+  default:     False
+  manual:      True
+
+source-repository head
+  type:     git
+  location: https://github.com/haskell/haskell-language-server
+
+library
+  exposed-modules:
+    Ide.Plugin.Eval
+    Ide.Plugin.Eval.Types
+
+  hs-source-dirs:     src
+  other-modules:
+    Ide.Plugin.Eval.Code
+    Ide.Plugin.Eval.CodeLens
+    Ide.Plugin.Eval.GHC
+    Ide.Plugin.Eval.Parse.Comments
+    Ide.Plugin.Eval.Parse.Option
+    Ide.Plugin.Eval.Util
+
+  build-depends:
+    , aeson
+    , base                  >=4.12    && <5
+    , containers
+    , deepseq
+    , Diff                  ^>=0.4.0
+    , directory
+    , dlist
+    , extra
+    , filepath
+    , ghc
+    , ghc-boot-th
+    , ghc-paths
+    , ghcide                ^>=1.2
+    , hashable
+    , hls-plugin-api        ^>=1.1
+    , lens
+    , lsp
+    , lsp-types
+    , megaparsec            >=9.0
+    , mtl
+    , parser-combinators
+    , pretty-simple
+    , QuickCheck
+    , safe-exceptions
+    , shake
+    , temporary
+    , text
+    , time
+    , transformers
+    , unliftio
+    , unordered-containers
+
+  ghc-options:
+    -Wall -Wno-name-shadowing -Wno-unticked-promoted-constructors
+
+  if flag(pedantic)
+    ghc-options: -Werror
+
+  default-language:   Haskell2010
+  default-extensions:
+    DataKinds
+    TypeOperators
+
+test-suite tests
+  type:             exitcode-stdio-1.0
+  default-language: Haskell2010
+  hs-source-dirs:   test
+  main-is:          Main.hs
+  ghc-options:      -threaded -rtsopts -with-rtsopts=-N
+  build-depends:
+    , aeson
+    , base
+    , directory
+    , extra
+    , filepath
+    , hls-eval-plugin
+    , hls-test-utils   ^>=1.0
+    , lens
+    , lsp-test
+    , lsp-types
+    , text
src/Ide/Plugin/Eval.hs view
@@ -1,28 +1,28 @@-{-# LANGUAGE DuplicateRecordFields #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# OPTIONS_GHC -Wwarn #-}--{- |-Eval Plugin entry point.--}-module Ide.Plugin.Eval (-    descriptor,-) where--import Development.IDE (IdeState)-import qualified Ide.Plugin.Eval.CodeLens as CL-import Ide.Types (-    PluginDescriptor (..),-    PluginId,-    defaultPluginDescriptor,-    mkPluginHandler- )-import Language.LSP.Types---- |Plugin descriptor-descriptor :: PluginId -> PluginDescriptor IdeState-descriptor plId =-    (defaultPluginDescriptor plId)-        { pluginHandlers = mkPluginHandler STextDocumentCodeLens CL.codeLens-        , pluginCommands = [CL.evalCommand]-        }+{-# LANGUAGE DuplicateRecordFields #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# OPTIONS_GHC -Wwarn #-}
+
+{- |
+Eval Plugin entry point.
+-}
+module Ide.Plugin.Eval (
+    descriptor,
+) where
+
+import Development.IDE (IdeState)
+import qualified Ide.Plugin.Eval.CodeLens as CL
+import Ide.Types (
+    PluginDescriptor (..),
+    PluginId,
+    defaultPluginDescriptor,
+    mkPluginHandler
+ )
+import Language.LSP.Types
+
+-- |Plugin descriptor
+descriptor :: PluginId -> PluginDescriptor IdeState
+descriptor plId =
+    (defaultPluginDescriptor plId)
+        { pluginHandlers = mkPluginHandler STextDocumentCodeLens CL.codeLens
+        , pluginCommands = [CL.evalCommand]
+        }
src/Ide/Plugin/Eval/Code.hs view
@@ -1,127 +1,131 @@-{-# LANGUAGE OverloadedStrings #-}-{-# OPTIONS_GHC -Wwarn -fno-warn-orphans #-}---- | Expression execution-module Ide.Plugin.Eval.Code (Statement, testRanges, resultRange, evalExtensions, evalSetup, evalExpr, propSetup, testCheck, asStatements) where--import Data.Algorithm.Diff (Diff, PolyDiff (..), getDiff)-import qualified Data.List.NonEmpty as NE-import Data.String (IsString)-import qualified Data.Text as T-import Development.IDE.Types.Location (Position (..), Range (..))-import GHC (compileExpr)-import GHC.LanguageExtensions.Type (Extension (..))-import GhcMonad (Ghc, GhcMonad, liftIO)-import Ide.Plugin.Eval.Types (-    Language (Plain),-    Loc,-    Section (sectionLanguage),-    Test (..),-    Txt,-    locate,-    locate0, Located(..)- )-import InteractiveEval (runDecls)-import Unsafe.Coerce (unsafeCoerce)-import Control.Lens ((^.))-import Language.LSP.Types.Lens (start, line)---- | Return the ranges of the expression and result parts of the given test-testRanges :: Test -> (Range, Range)-testRanges tst =-    let startLine = testRange tst ^. start.line-        (exprLines, resultLines) = testLenghts tst-        resLine = startLine + exprLines-     in ( Range-            (Position startLine 0)-            --(Position (startLine + exprLines + resultLines) 0),-            (Position resLine 0)-        , Range (Position resLine 0) (Position (resLine + resultLines) 0)-        )--{- |The document range where a test is defined- testRange :: Loc Test -> Range- testRange = fst . testRanges--}---- |The document range where the result of the test is defined-resultRange :: Test -> Range-resultRange = snd . testRanges---- TODO: handle BLANKLINE-{-->>> showDiffs $  getDiff ["abc","def","ghi","end"] ["abc","def","Z","ZZ","end"]-["abc","def","WAS ghi","NOW Z","NOW ZZ","end"]--}-showDiffs :: (Semigroup a, IsString a) => [Diff a] -> [a]-showDiffs = map showDiff--showDiff :: (Semigroup a, IsString a) => Diff a -> a-showDiff (First w) = "WAS " <> w-showDiff (Second w) = "NOW " <> w-showDiff (Both w _) = w--testCheck :: (Section, Test) -> [T.Text] -> [T.Text]-testCheck (section, test) out-    | null (testOutput test) || sectionLanguage section == Plain = out-    | otherwise = showDiffs $ getDiff (map T.pack $ testOutput test) out--testLenghts :: Test -> (Int, Int)-testLenghts (Example e r _) = (NE.length e, length r)-testLenghts (Property _ r _) = (1, length r)---- |A one-line Haskell statement-type Statement = Loc String--asStatements :: Test -> [Statement]-asStatements lt = locate $ Located (testRange lt ^. start.line) (asStmts lt)--asStmts :: Test -> [Txt]-asStmts (Example e _ _) = NE.toList e-asStmts (Property t _ _) =-    ["prop11 = " ++ t, "(propEvaluation prop11 :: IO String)"]---- |Evaluate an expression (either a pure expression or an IO a)-evalExpr :: GhcMonad m => [Char] -> m String-evalExpr e = do-    res <- compileExpr $ "asPrint (" ++ e ++ ")"-    liftIO (unsafeCoerce res :: IO String)---- |GHC extensions required for expression evaluation-evalExtensions :: [Extension]-evalExtensions =-    [ OverlappingInstances-    , UndecidableInstances-    , FlexibleInstances-    , IncoherentInstances-    , TupleSections-    ]---- |GHC declarations required for expression evaluation-evalSetup :: Ghc ()-evalSetup =-    mapM_-        runDecls-        [ "class Print f where asPrint :: f -> IO String"-        , "instance Show a => Print (IO a) where asPrint io = io >>= return . show"-        , "instance Show a => Print a where asPrint a = return (show a)"-        ]--{- |GHC declarations required to execute test properties--Example:--prop> \(l::[Bool]) -> reverse (reverse l) == l-+++ OK, passed 100 tests.--prop> \(l::[Bool]) -> reverse l == l-*** Failed! Falsified (after 6 tests and 2 shrinks):-[True,False]--}-propSetup :: [Loc [Char]]-propSetup =-    locate0-        [ ":set -XScopedTypeVariables -XExplicitForAll"-        , "import qualified Test.QuickCheck as Q11"-        , "propEvaluation p = Q11.quickCheckWithResult Q11.stdArgs p >>= error . Q11.output" -- uses `error` to get a multi-line display-        ]+{-# LANGUAGE LambdaCase        #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# OPTIONS_GHC -Wwarn -fno-warn-orphans #-}
+
+-- | Expression execution
+module Ide.Plugin.Eval.Code (Statement, testRanges, resultRange, evalExtensions, evalSetup, propSetup, testCheck, asStatements,myExecStmt) where
+
+import           Control.Lens                   ((^.))
+import           Data.Algorithm.Diff            (Diff, PolyDiff (..), getDiff)
+import qualified Data.List.NonEmpty             as NE
+import           Data.String                    (IsString)
+import qualified Data.Text                      as T
+import           Development.IDE.Types.Location (Position (..), Range (..))
+import           GHC                            (ExecOptions, ExecResult (..),
+                                                 execStmt)
+import           GHC.LanguageExtensions.Type    (Extension (..))
+import           GhcMonad                       (Ghc, liftIO, modifySession)
+import           HscTypes
+import           Ide.Plugin.Eval.Types          (Language (Plain), Loc,
+                                                 Located (..),
+                                                 Section (sectionLanguage),
+                                                 Test (..), Txt, locate,
+                                                 locate0)
+import           InteractiveEval                (getContext, parseImportDecl,
+                                                 runDecls, setContext)
+import           Language.LSP.Types.Lens        (line, start)
+import           System.IO.Extra                (newTempFile, readFile')
+
+-- | Return the ranges of the expression and result parts of the given test
+testRanges :: Test -> (Range, Range)
+testRanges tst =
+    let startLine = testRange tst ^. start.line
+        (exprLines, resultLines) = testLenghts tst
+        resLine = startLine + exprLines
+     in ( Range
+            (Position startLine 0)
+            --(Position (startLine + exprLines + resultLines) 0),
+            (Position resLine 0)
+        , Range (Position resLine 0) (Position (resLine + resultLines) 0)
+        )
+
+{- |The document range where a test is defined
+ testRange :: Loc Test -> Range
+ testRange = fst . testRanges
+-}
+
+-- |The document range where the result of the test is defined
+resultRange :: Test -> Range
+resultRange = snd . testRanges
+
+-- TODO: handle BLANKLINE
+{-
+>>> showDiffs $  getDiff ["abc","def","ghi","end"] ["abc","def","Z","ZZ","end"]
+["abc","def","WAS ghi","NOW Z","NOW ZZ","end"]
+-}
+showDiffs :: (Semigroup a, IsString a) => [Diff a] -> [a]
+showDiffs = map showDiff
+
+showDiff :: (Semigroup a, IsString a) => Diff a -> a
+showDiff (First w)  = "WAS " <> w
+showDiff (Second w) = "NOW " <> w
+showDiff (Both w _) = w
+
+testCheck :: (Section, Test) -> [T.Text] -> [T.Text]
+testCheck (section, test) out
+    | null (testOutput test) || sectionLanguage section == Plain = out
+    | otherwise = showDiffs $ getDiff (map T.pack $ testOutput test) out
+
+testLenghts :: Test -> (Int, Int)
+testLenghts (Example e r _)  = (NE.length e, length r)
+testLenghts (Property _ r _) = (1, length r)
+
+-- |A one-line Haskell statement
+type Statement = Loc String
+
+asStatements :: Test -> [Statement]
+asStatements lt = locate $ Located (testRange lt ^. start.line) (asStmts lt)
+
+asStmts :: Test -> [Txt]
+asStmts (Example e _ _) = NE.toList e
+asStmts (Property t _ _) =
+    ["prop11 = " ++ t, "(propEvaluation prop11 :: IO String)"]
+
+-- |GHC extensions required for expression evaluation
+evalExtensions :: [Extension]
+evalExtensions =
+    [ OverlappingInstances
+    , UndecidableInstances
+    , FlexibleInstances
+    , IncoherentInstances
+    , TupleSections
+    ]
+
+-- |GHC declarations required for expression evaluation
+evalSetup :: Ghc ()
+evalSetup = do
+    preludeAsP <- parseImportDecl "import qualified Prelude as P"
+    context <- getContext
+    setContext (IIDecl preludeAsP : context)
+
+-- | A wrapper of 'InteractiveEval.execStmt', capturing the execution result
+myExecStmt :: String -> ExecOptions -> Ghc (Either String (Maybe String))
+myExecStmt stmt opts = do
+    (temp, purge) <- liftIO newTempFile
+    evalPrint <- head <$> runDecls ("evalPrint x = P.writeFile "<> show temp <> " (P.show x)")
+    modifySession $ \hsc -> hsc {hsc_IC = setInteractivePrintName (hsc_IC hsc) evalPrint}
+    result <- execStmt stmt opts >>= \case
+              ExecComplete (Left err) _ -> pure $ Left $ show err
+              ExecComplete (Right _) _ -> liftIO $ Right . (\x -> if null x then Nothing else Just x) <$> readFile' temp
+              ExecBreak{} -> pure $ Right $ Just "breakpoints are not supported"
+    liftIO purge
+    pure result
+
+{- |GHC declarations required to execute test properties
+
+Example:
+
+prop> \(l::[Bool]) -> reverse (reverse l) == l
++++ OK, passed 100 tests.
+
+prop> \(l::[Bool]) -> reverse l == l
+*** Failed! Falsified (after 6 tests and 2 shrinks):
+[True,False]
+-}
+propSetup :: [Loc [Char]]
+propSetup =
+    locate0
+        [ ":set -XScopedTypeVariables -XExplicitForAll"
+        , "import qualified Test.QuickCheck as Q11"
+        , "propEvaluation p = Q11.quickCheckWithResult Q11.stdArgs p >>= error . Q11.output" -- uses `error` to get a multi-line display
+        ]
src/Ide/Plugin/Eval/CodeLens.hs view
@@ -1,767 +1,711 @@-{-# LANGUAGE TypeApplications #-}-{-# LANGUAGE ViewPatterns #-}-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE DuplicateRecordFields #-}-{-# LANGUAGE ExtendedDefaultRules #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE NamedFieldPuns #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE RecordWildCards #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE NoMonomorphismRestriction #-}-{-# OPTIONS_GHC -fno-warn-type-defaults #-}--{- |-A plugin inspired by the REPLoid feature of <https://github.com/jyp/dante Dante>, <https://www.haskell.org/haddock/doc/html/ch03s08.html#idm140354810775744 Haddock>'s Examples and Properties and <https://hackage.haskell.org/package/doctest Doctest>.--For a full example see the "Ide.Plugin.Eval.Tutorial" module.--}-module Ide.Plugin.Eval.CodeLens (-    codeLens,-    evalCommand,-) where--import Control.Applicative (Alternative ((<|>)))-import Control.Arrow (second, (>>>))-import qualified Control.Exception as E-import Control.Monad-    ( void,-      when, guard,-      join-    )-import Control.Monad.IO.Class (MonadIO (liftIO))-import Control.Monad.Trans.Except-    ( ExceptT (..),-    )-import Data.Aeson (toJSON)-import Data.Char (isSpace)-import qualified Data.HashMap.Strict as HashMap-import Data.List-    (dropWhileEnd,-      find, intercalate-    )-import qualified Data.Map.Strict as Map-import Data.Maybe-    ( catMaybes,-      fromMaybe,-    )-import Data.String (IsString)-import Data.Text (Text)-import qualified Data.Text as T-import Data.Time (getCurrentTime)-import Data.Typeable (Typeable)-import Development.IDE-    ( Action,-      realSrcSpanToRange,  GetModSummary (..),-      GetParsedModuleWithComments (..),-      HscEnvEq,-      IdeState,-      evalGhcEnv,-      hscEnvWithImportPaths,-      runAction,-      textToStringBuffer,-      toNormalizedFilePath',-      uriToFilePath',-      useWithStale_,-      prettyPrint,-      use_, useNoFile_, uses_,-      GhcSessionIO(..), GetDependencies(..), GetModIface(..),-      HiFileResult (hirHomeMod, hirModSummary)-    )-import Development.IDE.Core.Rules (TransitiveDependencies(transitiveModuleDeps))-import Development.IDE.Core.Compile (setupFinderCache, loadModulesHome)-import Development.IDE.GHC.Compat (AnnotationComment(AnnBlockComment, AnnLineComment), GenLocated (L), HscEnv, ParsedModule (..), SrcSpan (RealSrcSpan, UnhelpfulSpan), srcSpanFile, GhcException, setInteractiveDynFlags)-import Development.IDE.Types.Options-import DynamicLoading (initializePlugins)-import FastString (unpackFS)-import GHC-    (ExecOptions-        ( execLineNumber,-          execSourceFile-        ),-      ExecResult (..),-      GeneralFlag (..),-      Ghc,-      GhcLink (LinkInMemory),-      GhcMode (CompManager),-      GhcMonad (getSession),-      HscTarget (HscInterpreted),-      LoadHowMuch (LoadAllTargets),-      ModSummary (ms_hspp_opts),-      Module (moduleName),-      SuccessFlag (Failed, Succeeded),-      TcRnExprMode (..),-      execOptions,-      execStmt,-      exprType,-      getInteractiveDynFlags,-      getSessionDynFlags,-      isImport,-      isStmt,-      load,-      runDecls,-      setContext,-      setLogAction,-      setSessionDynFlags,-      setTargets,-      typeKind,-    )-import GhcPlugins-    ( DynFlags (..),-      hsc_dflags,-      defaultLogActionHPutStrDoc,-      gopt_set,-      gopt_unset,-      interpWays,-      targetPlatform,-      updateWays,-      wayGeneralFlags,-      wayUnsetGeneralFlags,-      xopt_set, parseDynamicFlagsCmdLine-    )-import HscTypes-    ( InteractiveImport (IIModule),-      ModSummary (ms_mod),-      Target (Target),-      TargetId (TargetFile),-    )-import Ide.Plugin.Eval.Code-    ( Statement,-      asStatements,-      evalExpr,-      evalExtensions,-      evalSetup,-      propSetup,-      resultRange,-      testCheck,-      testRanges,-    )-import Ide.Plugin.Eval.GHC-    ( addImport,-      addPackages,-      hasPackage,-      isExpr,-      showDynFlags,-    )-import Ide.Plugin.Eval.Parse.Comments (commentsToSections)-import Ide.Plugin.Eval.Parse.Option (parseSetFlags)-import Ide.Plugin.Eval.Types-import Ide.Plugin.Eval.Util-    ( asS,-      gStrictTry,-      handleMaybe,-      handleMaybeM,-      isLiterate,-      logWith,-      response,-      response',-      timed,-    )-import Ide.Types-import Language.LSP.Server-import Language.LSP.Types-import Language.LSP.VFS (virtualFileText)-import Outputable-    ( nest,-      ppr,-      showSDoc,-      text,-      ($$),-      (<+>),-    )-import System.FilePath (takeFileName)-import System.IO (hClose)-import UnliftIO.Temporary (withSystemTempFile)-import Util (OverridingBool (Never))-import Development.IDE.Core.PositionMapping (toCurrentRange)-import qualified Data.DList as DL-import Control.Lens ((^.), _1, (%~), (<&>), _3)-import Language.LSP.Types.Lens (line, end)-import CmdLineParser-import qualified Development.IDE.GHC.Compat as SrcLoc-import Control.Exception (try)--{- | Code Lens provider- NOTE: Invoked every time the document is modified, not just when the document is saved.--}-codeLens :: PluginMethodHandler IdeState TextDocumentCodeLens-codeLens st plId CodeLensParams{_textDocument} =-    let dbg = logWith st-        perf = timed dbg-     in perf "codeLens" $-            response $ do-                let TextDocumentIdentifier uri = _textDocument-                fp <- handleMaybe "uri" $ uriToFilePath' uri-                let nfp = toNormalizedFilePath' fp-                    isLHS = isLiterate fp-                dbg "fp" fp-                (ParsedModule{..}, posMap) <- liftIO $-                    runAction "parsed" st $ useWithStale_ GetParsedModuleWithComments nfp-                let comments = foldMap-                        ( foldMap $ \case-                            L (RealSrcSpan real) bdy-                                | unpackFS (srcSpanFile real) ==-                                    fromNormalizedFilePath nfp-                                , let ran0 = realSrcSpanToRange real-                                , Just curRan <- toCurrentRange posMap ran0-                                ->--                                    -- since Haddock parsing is unset explicitly in 'getParsedModuleWithComments',-                                    -- we can concentrate on these two-                                    case bdy of-                                        AnnLineComment cmt ->-                                            mempty { lineComments = Map.singleton curRan (RawLineComment cmt) }-                                        AnnBlockComment cmt ->-                                            mempty { blockComments = Map.singleton curRan $ RawBlockComment cmt }-                                        _ -> mempty-                            _ -> mempty-                        )-                        $ snd pm_annotations-                dbg "excluded comments" $ show $  DL.toList $-                    foldMap-                    (foldMap $ \(L a b) ->-                        case b of-                            AnnLineComment{} -> mempty-                            AnnBlockComment{} -> mempty-                            _ -> DL.singleton (a, b)-                    )-                    $ snd pm_annotations-                dbg "comments" $ show comments--                -- Extract tests from source code-                let Sections{..} = commentsToSections isLHS comments-                    tests = testsBySection nonSetupSections-                    cmd = mkLspCommand plId evalCommandName "Evaluate=..." (Just [])-                let lenses =-                        [ CodeLens testRange (Just cmd') Nothing-                        | (section, ident, test) <- tests-                        , let (testRange, resultRange) = testRanges test-                              args = EvalParams (setupSections ++ [section]) _textDocument ident-                              cmd' =-                                (cmd :: Command)-                                    { _arguments = Just (List [toJSON args])-                                    , _title =-                                        if trivial resultRange-                                            then "Evaluate..."-                                            else "Refresh..."-                                    }-                        ]--                perf "tests" $-                    dbg "Tests" $-                        unwords-                            [ show (length tests)-                            , "tests in"-                            , show (length nonSetupSections)-                            , "sections"-                            , show (length setupSections)-                            , "setups"-                            , show (length lenses)-                            , "lenses."-                            ]--                return $ List lenses-  where-    trivial (Range p p') = p == p'--evalCommandName :: CommandId-evalCommandName = "evalCommand"--evalCommand :: PluginCommand IdeState-evalCommand = PluginCommand evalCommandName "evaluate" runEvalCmd--type EvalId = Int--runEvalCmd :: CommandFunction IdeState EvalParams-runEvalCmd st EvalParams{..} =-    let dbg = logWith st-        perf = timed dbg-        cmd :: ExceptT String (LspM c) WorkspaceEdit-        cmd = do-            let tests = map (\(a,_,b) -> (a,b)) $ testsBySection sections--            let TextDocumentIdentifier{_uri} = module_-            fp <- handleMaybe "uri" $ uriToFilePath' _uri-            let nfp = toNormalizedFilePath' fp-            mdlText <- moduleText _uri--            session <- runGetSession st nfp--            (ms, _) <--                liftIO $-                    runAction "runEvalCmd.getModSummary" st $-                        use_ GetModSummary nfp--            now <- liftIO getCurrentTime--            let modName = moduleName $ ms_mod ms-                thisModuleTarget =-                    Target-                        (TargetFile fp Nothing)-                        False-                        (Just (textToStringBuffer mdlText, now))--            -- Setup environment for evaluation-            hscEnv' <- ExceptT $ fmap join $ withSystemTempFile (takeFileName fp) $ \logFilename logHandle -> liftIO . gStrictTry . evalGhcEnv session $ do-                env <- getSession--                -- Install the module pragmas and options-                df <- liftIO $ setupDynFlagsForGHCiLike env $ ms_hspp_opts ms--                -- Restore the original import paths-                let impPaths = importPaths $ hsc_dflags env-                df <- return df{importPaths = impPaths}--                -- Set the modified flags in the session-                _lp <- setSessionDynFlags df--                -- property tests need QuickCheck-                when (needsQuickCheck tests) $ void $ addPackages ["QuickCheck"]-                dbg "QUICKCHECK NEEDS" $ needsQuickCheck tests-                dbg "QUICKCHECK HAS" $ hasQuickCheck df--                -- copy the package state to the interactive DynFlags-                idflags <- getInteractiveDynFlags-                df <- getSessionDynFlags-                setInteractiveDynFlags $-                    (foldl xopt_set idflags evalExtensions)-                        { pkgState =-                            pkgState-                                df-                        , pkgDatabase =-                            pkgDatabase-                                df-                        , packageFlags =-                            packageFlags-                                df-                        , useColor = Never-                        , canUseColor = False-                        }--                -- set up a custom log action-                setLogAction $ \_df _wr _sev _span _style _doc ->-                    defaultLogActionHPutStrDoc _df logHandle _doc _style--                -- Load the module with its current content (as the saved module might not be up to date)-                -- BUG: this fails for files that requires preprocessors (e.g. CPP) for ghc < 8.8-                -- see https://gitlab.haskell.org/ghc/ghc/-/issues/17066-                -- and https://hackage.haskell.org/package/ghc-8.10.1/docs/GHC.html#v:TargetFile-                eSetTarget <- gStrictTry $ setTargets [thisModuleTarget]-                dbg "setTarget" eSetTarget--                -- load the module in the interactive environment-                loadResult <- perf "loadModule" $ load LoadAllTargets-                dbg "LOAD RESULT" $ asS loadResult-                case loadResult of-                    Failed -> liftIO $ do-                        hClose logHandle-                        err <- readFile logFilename-                        dbg "load ERR" err-                        return $ Left err-                    Succeeded -> do-                        -- Evaluation takes place 'inside' the module-                        setContext [IIModule modName]-                        Right <$> getSession--            edits <--                perf "edits" $-                    liftIO $-                        evalGhcEnv hscEnv' $-                            runTests-                                (st, fp)-                                tests--            let workspaceEditsMap = HashMap.fromList [(_uri, List $ addFinalReturn mdlText edits)]-            let workspaceEdits = WorkspaceEdit (Just workspaceEditsMap) Nothing--            return workspaceEdits-     in perf "evalCmd" $-            withIndefiniteProgress "Evaluating" Cancellable $-                response' cmd--addFinalReturn :: Text -> [TextEdit] -> [TextEdit]-addFinalReturn mdlText edits-    | not (null edits) && not (T.null mdlText) && T.last mdlText /= '\n' =-        finalReturn mdlText : edits-    | otherwise = edits--finalReturn :: Text -> TextEdit-finalReturn txt =-    let ls = T.lines txt-        l = length ls -1-        c = T.length . last $ ls-        p = Position l c-     in TextEdit (Range p p) "\n"--moduleText :: (IsString e, MonadLsp c m) => Uri -> ExceptT e m Text-moduleText uri =-    handleMaybeM "mdlText" $-      (virtualFileText <$>)-          <$> getVirtualFile-              (toNormalizedUri uri)--testsBySection :: [Section] -> [(Section, EvalId, Test)]-testsBySection sections =-    [(section, ident, test)-    | (ident, section) <- zip [0..] sections-    , test <- sectionTests section-    ]--type TEnv = (IdeState, String)--runTests :: TEnv -> [(Section, Test)] -> Ghc [TextEdit]-runTests e@(_st, _) tests = do-    df <- getInteractiveDynFlags-    evalSetup-    when (hasQuickCheck df && needsQuickCheck tests) $ void $ evals e df propSetup--    mapM (processTest e df) tests-  where-    processTest :: TEnv -> DynFlags -> (Section, Test) -> Ghc TextEdit-    processTest e@(st, fp) df (section, test) = do-        let dbg = logWith st-        let pad = pad_ $ (if isLiterate fp then ("> " `T.append`) else id) $ padPrefix (sectionFormat section)--        rs <- runTest e df test-        dbg "TEST RESULTS" rs--        let checkedResult = testCheck (section, test) rs--        let edit = asEdit (sectionFormat section) test (map pad checkedResult)-        dbg "TEST EDIT" edit-        return edit--    -- runTest :: String -> DynFlags -> Loc Test -> Ghc [Text]-    runTest _ df test-        | not (hasQuickCheck df) && isProperty test =-            return $-                singleLine-                    "Add QuickCheck to your cabal dependencies to run this test."-    runTest e df test = evals e df (asStatements test)--asEdit :: Format -> Test -> [Text] -> TextEdit-asEdit (MultiLine commRange) test resultLines-    -- A test in a block comment, ending with @-\}@ without newline in-between.-    | testRange test ^. end.line == commRange ^. end . line-    =-    TextEdit-        (Range-            (testRange test ^. end)-            (resultRange test ^. end)-        )-        ("\n" <> T.unlines (resultLines <> ["-}"]))-asEdit _ test resultLines =-    TextEdit (resultRange test) (T.unlines resultLines)--{--The result of evaluating a test line can be:-* a value-* nothing-* a (possibly multiline) error message--A value is returned for a correct expression.--Either a pure value:->>> 'h' :"askell"-"haskell"--Or an 'IO a' (output on stdout/stderr is ignored):->>> print "OK" >> return "ABC"-"ABC"--Nothing is returned for a correct directive:-->>>:set -XFlexibleInstances->>> import Data.Maybe--Nothing is returned for a correct declaration (let..,x=, data, class)-->>> let x = 11->>> y = 22->>> data B = T | F->>> class C a--Nothing is returned for an empty line:-->>>--A, possibly multi line, error is returned for a wrong declaration, directive or value or an exception thrown by the evaluated code:-->>>:set -XNonExistent-Unknown extension: "NonExistent"-->>> cls C-Variable not in scope: cls :: t0 -> ()-Data constructor not in scope: C-->>> "A-lexical error in string/character literal at end of input-->>> 3 `div` 0-divide by zero-->>> error "Something went wrong\nbad times" :: E.SomeException-Something went wrong-bad times--Or for a value that does not have a Show instance and can therefore not be displayed:->>> data V = V->>> V-No instance for (Show V)--}-evals :: TEnv -> DynFlags -> [Statement] -> Ghc [Text]-evals (st, fp) df stmts = do-    er <- gStrictTry $ mapM eval stmts-    return $ case er of-        Left err -> errorLines err-        Right rs -> concat . catMaybes $ rs-  where-    dbg = logWith st-    eval :: Statement -> Ghc (Maybe [Text])-    eval (Located l stmt)-        | -- GHCi flags-          Just (words -> flags) <- parseSetFlags stmt = do-            dbg "{:SET" flags-            ndf <- getInteractiveDynFlags-            dbg "pre set" $ showDynFlags ndf-            eans <--                liftIO $ try @GhcException $-                parseDynamicFlagsCmdLine ndf-                (map (L $ UnhelpfulSpan "<interactive>") flags)-            dbg "parsed flags" $ eans-              <&> (_1 %~ showDynFlags >>> _3 %~ map warnMsg)-            case eans of-                Left err -> pure $ Just $ errorLines $ show err-                Right (df', ignoreds, warns) -> do-                    let warnings = do-                            guard $ not $ null warns-                            pure $ errorLines $-                                unlines $-                                map prettyWarn warns-                        igns = do-                            guard $ not $ null ignoreds-                            pure-                                ["Some flags have not been recognized: "-                                <> T.pack (intercalate ", " $ map SrcLoc.unLoc ignoreds)-                                ]-                    dbg "post set" $ showDynFlags df'-                    _ <- setSessionDynFlags df'-                    sessDyns <- getSessionDynFlags-                    setInteractiveDynFlags sessDyns-                    pure $ warnings <> igns-        | -- A type/kind command-          Just (cmd, arg) <- parseGhciLikeCmd $ T.pack stmt =-            evalGhciLikeCmd cmd arg-        | -- An expression-          isExpr df stmt =-            do-                dbg "{EXPR" stmt-                eres <- gStrictTry $ evalExpr stmt-                dbg "RES ->" eres-                let res = case eres of-                        Left err -> errorLines err-                        Right rs -> [T.pack rs]-                dbg "EXPR} ->" res-                return . Just $ res-        | -- A statement-          isStmt df stmt =-            do-                dbg "{STMT " stmt-                res <- exec stmt l-                r <- case res of-                    ExecComplete (Left err) _ -> return . Just . errorLines . show $ err-                    ExecComplete (Right _) _ -> return Nothing-                    ExecBreak{} ->-                        return . Just . singleLine $ "breakpoints are not supported"-                dbg "STMT} -> " r-                return r-        | -- An import-          isImport df stmt =-            do-                dbg "{IMPORT " stmt-                _ <- addImport stmt-                return Nothing-        | -- A declaration-          otherwise =-            do-                dbg "{DECL " stmt-                void $ runDecls stmt-                return Nothing-    exec stmt l =-        let opts = execOptions{execSourceFile = fp, execLineNumber = l}-         in execStmt stmt opts--prettyWarn :: Warn -> String-prettyWarn Warn{..} =-    prettyPrint (SrcLoc.getLoc warnMsg) <> ": warning:\n"-    <> "    " <> SrcLoc.unLoc warnMsg--ghcSessionDepsDefinition :: HscEnvEq -> NormalizedFilePath -> Action HscEnv-ghcSessionDepsDefinition env file = do-        let hsc = hscEnvWithImportPaths env-        deps <- use_ GetDependencies file-        let tdeps = transitiveModuleDeps deps-        ifaces <- uses_ GetModIface tdeps--        -- Currently GetDependencies returns things in topological order so A comes before B if A imports B.-        -- We need to reverse this as GHC gets very unhappy otherwise and complains about broken interfaces.-        -- Long-term we might just want to change the order returned by GetDependencies-        let inLoadOrder = reverse (map hirHomeMod ifaces)--        liftIO $ loadModulesHome inLoadOrder <$> setupFinderCache (map hirModSummary ifaces) hsc--runGetSession :: MonadIO m => IdeState -> NormalizedFilePath -> m HscEnv-runGetSession st nfp = liftIO $ runAction "eval" st $ do-    -- Create a new GHC Session rather than reusing an existing one-    -- to avoid interfering with ghcide-    IdeGhcSession{loadSessionFun} <- useNoFile_ GhcSessionIO-    let fp = fromNormalizedFilePath nfp-    ((_, res),_) <- liftIO $ loadSessionFun fp-    let hscEnv = fromMaybe (error $ "Unknown file: " <> fp) res-    ghcSessionDepsDefinition hscEnv nfp--needsQuickCheck :: [(Section, Test)] -> Bool-needsQuickCheck = any (isProperty . snd)--hasQuickCheck :: DynFlags -> Bool-hasQuickCheck df = hasPackage df "QuickCheck"--singleLine :: String -> [Text]-singleLine s = [T.pack s]--{- |- Convert error messages to a list of text lines- Remove unnecessary information.--}-errorLines :: String -> [Text]-errorLines =-    map (\e -> fromMaybe e (T.stripSuffix "arising from a use of ‘asPrint’" e))-        . dropWhileEnd T.null-        . takeWhile (not . ("CallStack" `T.isPrefixOf`))-        . T.lines-        . T.pack--{- |->>> map (pad_ (T.pack "--")) (map T.pack ["2+2",""])-["--2+2","--<BLANKLINE>"]--}-pad_ :: Text -> Text -> Text-pad_ prefix = (prefix `T.append`) . convertBlank--convertBlank :: Text -> Text-convertBlank x-    | T.null x = "<BLANKLINE>"-    | otherwise = x--padPrefix :: IsString p => Format -> p-padPrefix SingleLine = "-- "-padPrefix _ = ""--{- | Resulting @Text@ MUST NOT prefix each line with @--@-   Such comment-related post-process will be taken place-   solely in 'evalGhciLikeCmd'.--}-type GHCiLikeCmd = DynFlags -> Text -> Ghc (Maybe Text)---- Should we use some sort of trie here?-ghciLikeCommands :: [(Text, GHCiLikeCmd)]-ghciLikeCommands =-    [("kind", doKindCmd False), ("kind!", doKindCmd True), ("type", doTypeCmd)]--evalGhciLikeCmd :: Text -> Text -> Ghc (Maybe [Text])-evalGhciLikeCmd cmd arg = do-    df <- getSessionDynFlags-    case lookup cmd ghciLikeCommands-        <|> snd-        <$> find (T.isPrefixOf cmd . fst) ghciLikeCommands of-        Just hndler ->-            fmap-                T.lines-                <$> hndler df arg-        _ -> E.throw $ GhciLikeCmdNotImplemented cmd arg--doKindCmd :: Bool -> DynFlags -> Text -> Ghc (Maybe Text)-doKindCmd False df arg = do-    let input = T.strip arg-    (_, kind) <- typeKind False $ T.unpack input-    let kindText = text (T.unpack input) <+> "::" <+> ppr kind-    pure $ Just $ T.pack (showSDoc df kindText)-doKindCmd True df arg = do-    let input = T.strip arg-    (ty, kind) <- typeKind True $ T.unpack input-    let kindDoc = text (T.unpack input) <+> "::" <+> ppr kind-        tyDoc = "=" <+> ppr ty-    pure $ Just $ T.pack (showSDoc df $ kindDoc $$ tyDoc)--doTypeCmd :: DynFlags -> Text -> Ghc (Maybe Text)-doTypeCmd dflags arg = do-    let (emod, expr) = parseExprMode arg-    ty <- exprType emod $ T.unpack expr-    let rawType = T.strip $ T.pack $ showSDoc dflags $ ppr ty-        broken = T.any (\c -> c == '\r' || c == '\n') rawType-    pure $-        Just $-            if broken-                then-                    T.pack $-                        showSDoc dflags $-                            text (T.unpack expr)-                                $$ nest 2 ("::" <+> ppr ty)-                else expr <> " :: " <> rawType <> "\n"--parseExprMode :: Text -> (TcRnExprMode, T.Text)-parseExprMode rawArg = case T.break isSpace rawArg of-    ("+v", rest) -> (TM_NoInst, T.strip rest)-    ("+d", rest) -> (TM_Default, T.strip rest)-    _ -> (TM_Inst, rawArg)--data GhciLikeCmdException = GhciLikeCmdNotImplemented-    { ghciCmdName :: Text-    , ghciCmdArg :: Text-    }-    deriving (Typeable)--instance Show GhciLikeCmdException where-    showsPrec _ GhciLikeCmdNotImplemented{..} =-        showString "unknown command '"-            . showString (T.unpack ghciCmdName)-            . showChar '\''--instance E.Exception GhciLikeCmdException--{-->>> parseGhciLikeCmd (T.pack ":kind! N + M + 1")-Just ("kind!","N + M + 1")->>> parseGhciLikeCmd (T.pack ":kind a")-Just ("kind","a")--}-parseGhciLikeCmd :: Text -> Maybe (Text, Text)-parseGhciLikeCmd input = do-    (':', rest) <- T.uncons $ T.stripStart input-    pure $ second T.strip $ T.break isSpace rest--setupDynFlagsForGHCiLike :: HscEnv -> DynFlags -> IO DynFlags-setupDynFlagsForGHCiLike env dflags = do-    let dflags3 =-            dflags-                { hscTarget = HscInterpreted-                , ghcMode = CompManager-                , ghcLink = LinkInMemory-                }-        platform = targetPlatform dflags3-        dflags3a = updateWays $ dflags3{ways = interpWays}-        dflags3b =-            foldl gopt_set dflags3a $-                concatMap (wayGeneralFlags platform) interpWays-        dflags3c =-            foldl gopt_unset dflags3b $-                concatMap (wayUnsetGeneralFlags platform) interpWays-        dflags4 =-            dflags3c-                `gopt_set` Opt_ImplicitImportQualified-                `gopt_set` Opt_IgnoreOptimChanges-                `gopt_set` Opt_IgnoreHpcChanges-                `gopt_unset` Opt_DiagnosticsShowCaret-    initializePlugins env dflags4+{-# LANGUAGE DuplicateRecordFields     #-}
+{-# LANGUAGE ExtendedDefaultRules      #-}
+{-# LANGUAGE FlexibleContexts          #-}
+{-# LANGUAGE FlexibleInstances         #-}
+{-# LANGUAGE LambdaCase                #-}
+{-# LANGUAGE NamedFieldPuns            #-}
+{-# LANGUAGE NoMonomorphismRestriction #-}
+{-# LANGUAGE OverloadedStrings         #-}
+{-# LANGUAGE RankNTypes                #-}
+{-# LANGUAGE RecordWildCards           #-}
+{-# LANGUAGE ScopedTypeVariables       #-}
+{-# LANGUAGE TypeApplications          #-}
+{-# LANGUAGE ViewPatterns              #-}
+{-# OPTIONS_GHC -fno-warn-type-defaults #-}
+
+{- |
+A plugin inspired by the REPLoid feature of <https://github.com/jyp/dante Dante>, <https://www.haskell.org/haddock/doc/html/ch03s08.html#idm140354810775744 Haddock>'s Examples and Properties and <https://hackage.haskell.org/package/doctest Doctest>.
+
+For a full example see the "Ide.Plugin.Eval.Tutorial" module.
+-}
+module Ide.Plugin.Eval.CodeLens (
+    codeLens,
+    evalCommand,
+) where
+
+import           CmdLineParser
+import           Control.Applicative                  (Alternative ((<|>)))
+import           Control.Arrow                        (second, (>>>))
+import           Control.Exception                    (try)
+import qualified Control.Exception                    as E
+import           Control.Lens                         (_1, _3, (%~), (<&>),
+                                                       (^.))
+import           Control.Monad                        (guard, join, void, when)
+import           Control.Monad.IO.Class               (MonadIO (liftIO))
+import           Control.Monad.Trans.Except           (ExceptT (..))
+import           Data.Aeson                           (toJSON)
+import           Data.Char                            (isSpace)
+import qualified Data.DList                           as DL
+import qualified Data.HashMap.Strict                  as HashMap
+import           Data.List                            (dropWhileEnd, find,
+                                                       intercalate)
+import qualified Data.Map.Strict                      as Map
+import           Data.Maybe                           (catMaybes, fromMaybe)
+import           Data.String                          (IsString)
+import           Data.Text                            (Text)
+import qualified Data.Text                            as T
+import           Data.Time                            (getCurrentTime)
+import           Data.Typeable                        (Typeable)
+import           Development.IDE                      (Action,
+                                                       GetDependencies (..),
+                                                       GetModIface (..),
+                                                       GetModSummary (..),
+                                                       GetParsedModuleWithComments (..),
+                                                       GhcSessionIO (..),
+                                                       HiFileResult (hirHomeMod, hirModSummary),
+                                                       HscEnvEq, IdeState,
+                                                       ModSummaryResult (..),
+                                                       evalGhcEnv,
+                                                       hscEnvWithImportPaths,
+                                                       prettyPrint,
+                                                       realSrcSpanToRange,
+                                                       runAction,
+                                                       textToStringBuffer,
+                                                       toNormalizedFilePath',
+                                                       uriToFilePath',
+                                                       useNoFile_,
+                                                       useWithStale_, use_,
+                                                       uses_)
+import           Development.IDE.Core.Compile         (loadModulesHome,
+                                                       setupFinderCache)
+import           Development.IDE.Core.PositionMapping (toCurrentRange)
+import           Development.IDE.Core.Rules           (TransitiveDependencies (transitiveModuleDeps))
+import           Development.IDE.GHC.Compat           (AnnotationComment (AnnBlockComment, AnnLineComment),
+                                                       GenLocated (L),
+                                                       GhcException, HscEnv,
+                                                       ParsedModule (..),
+                                                       SrcSpan (RealSrcSpan, UnhelpfulSpan),
+                                                       setInteractiveDynFlags,
+                                                       srcSpanFile)
+import qualified Development.IDE.GHC.Compat           as SrcLoc
+import           Development.IDE.Types.Options
+import           DynamicLoading                       (initializePlugins)
+import           FastString                           (unpackFS)
+import           GHC                                  (ExecOptions (execLineNumber, execSourceFile),
+                                                       GeneralFlag (..), Ghc,
+                                                       GhcLink (LinkInMemory),
+                                                       GhcMode (CompManager),
+                                                       GhcMonad (getSession),
+                                                       HscTarget (HscInterpreted),
+                                                       LoadHowMuch (LoadAllTargets),
+                                                       ModSummary (ms_hspp_opts),
+                                                       Module (moduleName),
+                                                       SuccessFlag (Failed, Succeeded),
+                                                       TcRnExprMode (..),
+                                                       execOptions, exprType,
+                                                       getInteractiveDynFlags,
+                                                       getSessionDynFlags,
+                                                       isImport, isStmt, load,
+                                                       runDecls, setContext,
+                                                       setLogAction,
+                                                       setSessionDynFlags,
+                                                       setTargets, typeKind)
+import           GhcPlugins                           (DynFlags (..),
+                                                       defaultLogActionHPutStrDoc,
+                                                       gopt_set, gopt_unset,
+                                                       hsc_dflags, interpWays,
+                                                       parseDynamicFlagsCmdLine,
+                                                       targetPlatform,
+                                                       updateWays,
+                                                       wayGeneralFlags,
+                                                       wayUnsetGeneralFlags,
+                                                       xopt_set)
+import           HscTypes                             (InteractiveImport (IIModule),
+                                                       ModSummary (ms_mod),
+                                                       Target (Target),
+                                                       TargetId (TargetFile))
+import           Ide.Plugin.Eval.Code                 (Statement, asStatements,
+                                                       evalExtensions,
+                                                       evalSetup, myExecStmt,
+                                                       propSetup, resultRange,
+                                                       testCheck, testRanges)
+import           Ide.Plugin.Eval.GHC                  (addImport, addPackages,
+                                                       hasPackage, showDynFlags)
+import           Ide.Plugin.Eval.Parse.Comments       (commentsToSections)
+import           Ide.Plugin.Eval.Parse.Option         (parseSetFlags)
+import           Ide.Plugin.Eval.Types
+import           Ide.Plugin.Eval.Util                 (asS, gStrictTry,
+                                                       handleMaybe,
+                                                       handleMaybeM, isLiterate,
+                                                       logWith, response,
+                                                       response', timed)
+import           Ide.Types
+import           Language.LSP.Server
+import           Language.LSP.Types
+import           Language.LSP.Types.Lens              (end, line)
+import           Language.LSP.VFS                     (virtualFileText)
+import           Outputable                           (nest, ppr, showSDoc,
+                                                       text, ($$), (<+>))
+import           System.FilePath                      (takeFileName)
+import           System.IO                            (hClose)
+import           UnliftIO.Temporary                   (withSystemTempFile)
+import           Util                                 (OverridingBool (Never))
+
+{- | Code Lens provider
+ NOTE: Invoked every time the document is modified, not just when the document is saved.
+-}
+codeLens :: PluginMethodHandler IdeState TextDocumentCodeLens
+codeLens st plId CodeLensParams{_textDocument} =
+    let dbg = logWith st
+        perf = timed dbg
+     in perf "codeLens" $
+            response $ do
+                let TextDocumentIdentifier uri = _textDocument
+                fp <- handleMaybe "uri" $ uriToFilePath' uri
+                let nfp = toNormalizedFilePath' fp
+                    isLHS = isLiterate fp
+                dbg "fp" fp
+                (ParsedModule{..}, posMap) <- liftIO $
+                    runAction "parsed" st $ useWithStale_ GetParsedModuleWithComments nfp
+                let comments = foldMap
+                        ( foldMap $ \case
+                            L (RealSrcSpan real) bdy
+                                | unpackFS (srcSpanFile real) ==
+                                    fromNormalizedFilePath nfp
+                                , let ran0 = realSrcSpanToRange real
+                                , Just curRan <- toCurrentRange posMap ran0
+                                ->
+
+                                    -- since Haddock parsing is unset explicitly in 'getParsedModuleWithComments',
+                                    -- we can concentrate on these two
+                                    case bdy of
+                                        AnnLineComment cmt ->
+                                            mempty { lineComments = Map.singleton curRan (RawLineComment cmt) }
+                                        AnnBlockComment cmt ->
+                                            mempty { blockComments = Map.singleton curRan $ RawBlockComment cmt }
+                                        _ -> mempty
+                            _ -> mempty
+                        )
+                        $ snd pm_annotations
+                dbg "excluded comments" $ show $  DL.toList $
+                    foldMap
+                    (foldMap $ \(L a b) ->
+                        case b of
+                            AnnLineComment{}  -> mempty
+                            AnnBlockComment{} -> mempty
+                            _                 -> DL.singleton (a, b)
+                    )
+                    $ snd pm_annotations
+                dbg "comments" $ show comments
+
+                -- Extract tests from source code
+                let Sections{..} = commentsToSections isLHS comments
+                    tests = testsBySection nonSetupSections
+                    cmd = mkLspCommand plId evalCommandName "Evaluate=..." (Just [])
+                let lenses =
+                        [ CodeLens testRange (Just cmd') Nothing
+                        | (section, ident, test) <- tests
+                        , let (testRange, resultRange) = testRanges test
+                              args = EvalParams (setupSections ++ [section]) _textDocument ident
+                              cmd' =
+                                (cmd :: Command)
+                                    { _arguments = Just (List [toJSON args])
+                                    , _title =
+                                        if trivial resultRange
+                                            then "Evaluate..."
+                                            else "Refresh..."
+                                    }
+                        ]
+
+                perf "tests" $
+                    dbg "Tests" $
+                        unwords
+                            [ show (length tests)
+                            , "tests in"
+                            , show (length nonSetupSections)
+                            , "sections"
+                            , show (length setupSections)
+                            , "setups"
+                            , show (length lenses)
+                            , "lenses."
+                            ]
+
+                return $ List lenses
+  where
+    trivial (Range p p') = p == p'
+
+evalCommandName :: CommandId
+evalCommandName = "evalCommand"
+
+evalCommand :: PluginCommand IdeState
+evalCommand = PluginCommand evalCommandName "evaluate" runEvalCmd
+
+type EvalId = Int
+
+runEvalCmd :: CommandFunction IdeState EvalParams
+runEvalCmd st EvalParams{..} =
+    let dbg = logWith st
+        perf = timed dbg
+        cmd :: ExceptT String (LspM c) WorkspaceEdit
+        cmd = do
+            let tests = map (\(a,_,b) -> (a,b)) $ testsBySection sections
+
+            let TextDocumentIdentifier{_uri} = module_
+            fp <- handleMaybe "uri" $ uriToFilePath' _uri
+            let nfp = toNormalizedFilePath' fp
+            mdlText <- moduleText _uri
+
+            session <- runGetSession st nfp
+
+            ms <- fmap msrModSummary $
+                liftIO $
+                    runAction "runEvalCmd.getModSummary" st $
+                        use_ GetModSummary nfp
+
+            now <- liftIO getCurrentTime
+
+            let modName = moduleName $ ms_mod ms
+                thisModuleTarget =
+                    Target
+                        (TargetFile fp Nothing)
+                        False
+                        (Just (textToStringBuffer mdlText, now))
+
+            -- Setup environment for evaluation
+            hscEnv' <- ExceptT $ fmap join $ withSystemTempFile (takeFileName fp) $ \logFilename logHandle -> liftIO . gStrictTry . evalGhcEnv session $ do
+                env <- getSession
+
+                -- Install the module pragmas and options
+                df <- liftIO $ setupDynFlagsForGHCiLike env $ ms_hspp_opts ms
+
+                -- Restore the original import paths
+                let impPaths = importPaths $ hsc_dflags env
+                df <- return df{importPaths = impPaths}
+
+                -- Set the modified flags in the session
+                _lp <- setSessionDynFlags df
+
+                -- property tests need QuickCheck
+                when (needsQuickCheck tests) $ void $ addPackages ["QuickCheck"]
+                dbg "QUICKCHECK NEEDS" $ needsQuickCheck tests
+                dbg "QUICKCHECK HAS" $ hasQuickCheck df
+
+                -- copy the package state to the interactive DynFlags
+                idflags <- getInteractiveDynFlags
+                df <- getSessionDynFlags
+                setInteractiveDynFlags $
+                    (foldl xopt_set idflags evalExtensions)
+                        { pkgState =
+                            pkgState
+                                df
+                        , pkgDatabase =
+                            pkgDatabase
+                                df
+                        , packageFlags =
+                            packageFlags
+                                df
+                        , useColor = Never
+                        , canUseColor = False
+                        }
+
+                -- set up a custom log action
+                setLogAction $ \_df _wr _sev _span _style _doc ->
+                    defaultLogActionHPutStrDoc _df logHandle _doc _style
+
+                -- Load the module with its current content (as the saved module might not be up to date)
+                -- BUG: this fails for files that requires preprocessors (e.g. CPP) for ghc < 8.8
+                -- see https://gitlab.haskell.org/ghc/ghc/-/issues/17066
+                -- and https://hackage.haskell.org/package/ghc-8.10.1/docs/GHC.html#v:TargetFile
+                eSetTarget <- gStrictTry $ setTargets [thisModuleTarget]
+                dbg "setTarget" eSetTarget
+
+                -- load the module in the interactive environment
+                loadResult <- perf "loadModule" $ load LoadAllTargets
+                dbg "LOAD RESULT" $ asS loadResult
+                case loadResult of
+                    Failed -> liftIO $ do
+                        hClose logHandle
+                        err <- readFile logFilename
+                        dbg "load ERR" err
+                        return $ Left err
+                    Succeeded -> do
+                        -- Evaluation takes place 'inside' the module
+                        setContext [IIModule modName]
+                        Right <$> getSession
+
+            edits <-
+                perf "edits" $
+                    liftIO $
+                        evalGhcEnv hscEnv' $
+                            runTests
+                                (st, fp)
+                                tests
+
+            let workspaceEditsMap = HashMap.fromList [(_uri, List $ addFinalReturn mdlText edits)]
+            let workspaceEdits = WorkspaceEdit (Just workspaceEditsMap) Nothing Nothing
+
+            return workspaceEdits
+     in perf "evalCmd" $
+            withIndefiniteProgress "Evaluating" Cancellable $
+                response' cmd
+
+addFinalReturn :: Text -> [TextEdit] -> [TextEdit]
+addFinalReturn mdlText edits
+    | not (null edits) && not (T.null mdlText) && T.last mdlText /= '\n' =
+        finalReturn mdlText : edits
+    | otherwise = edits
+
+finalReturn :: Text -> TextEdit
+finalReturn txt =
+    let ls = T.lines txt
+        l = length ls -1
+        c = T.length . last $ ls
+        p = Position l c
+     in TextEdit (Range p p) "\n"
+
+moduleText :: (IsString e, MonadLsp c m) => Uri -> ExceptT e m Text
+moduleText uri =
+    handleMaybeM "mdlText" $
+      (virtualFileText <$>)
+          <$> getVirtualFile
+              (toNormalizedUri uri)
+
+testsBySection :: [Section] -> [(Section, EvalId, Test)]
+testsBySection sections =
+    [(section, ident, test)
+    | (ident, section) <- zip [0..] sections
+    , test <- sectionTests section
+    ]
+
+type TEnv = (IdeState, String)
+
+runTests :: TEnv -> [(Section, Test)] -> Ghc [TextEdit]
+runTests e@(_st, _) tests = do
+    df <- getInteractiveDynFlags
+    evalSetup
+    when (hasQuickCheck df && needsQuickCheck tests) $ void $ evals e df propSetup
+
+    mapM (processTest e df) tests
+  where
+    processTest :: TEnv -> DynFlags -> (Section, Test) -> Ghc TextEdit
+    processTest e@(st, fp) df (section, test) = do
+        let dbg = logWith st
+        let pad = pad_ $ (if isLiterate fp then ("> " `T.append`) else id) $ padPrefix (sectionFormat section)
+
+        rs <- runTest e df test
+        dbg "TEST RESULTS" rs
+
+        let checkedResult = testCheck (section, test) rs
+
+        let edit = asEdit (sectionFormat section) test (map pad checkedResult)
+        dbg "TEST EDIT" edit
+        return edit
+
+    -- runTest :: String -> DynFlags -> Loc Test -> Ghc [Text]
+    runTest _ df test
+        | not (hasQuickCheck df) && isProperty test =
+            return $
+                singleLine
+                    "Add QuickCheck to your cabal dependencies to run this test."
+    runTest e df test = evals e df (asStatements test)
+
+asEdit :: Format -> Test -> [Text] -> TextEdit
+asEdit (MultiLine commRange) test resultLines
+    -- A test in a block comment, ending with @-\}@ without newline in-between.
+    | testRange test ^. end.line == commRange ^. end . line
+    =
+    TextEdit
+        (Range
+            (testRange test ^. end)
+            (resultRange test ^. end)
+        )
+        ("\n" <> T.unlines (resultLines <> ["-}"]))
+asEdit _ test resultLines =
+    TextEdit (resultRange test) (T.unlines resultLines)
+
+{-
+The result of evaluating a test line can be:
+* a value
+* nothing
+* a (possibly multiline) error message
+
+A value is returned for a correct expression.
+
+Either a pure value:
+>>> 'h' :"askell"
+"haskell"
+
+Or an 'IO a' (output on stdout/stderr is ignored):
+>>> print "OK" >> return "ABC"
+"ABC"
+
+Nothing is returned for a correct directive:
+
+>>>:set -XFlexibleInstances
+>>> import Data.Maybe
+
+Nothing is returned for a correct declaration (let..,x=, data, class)
+
+>>> let x = 11
+>>> y = 22
+>>> data B = T | F
+>>> class C a
+
+Nothing is returned for an empty line:
+
+>>>
+
+A, possibly multi line, error is returned for a wrong declaration, directive or value or an exception thrown by the evaluated code:
+
+>>>:set -XNonExistent
+Unknown extension: "NonExistent"
+
+>>> cls C
+Variable not in scope: cls :: t0 -> ()
+Data constructor not in scope: C
+
+>>> "A
+lexical error in string/character literal at end of input
+
+>>> 3 `div` 0
+divide by zero
+
+>>> error "Something went wrong\nbad times" :: E.SomeException
+Something went wrong
+bad times
+
+Or for a value that does not have a Show instance and can therefore not be displayed:
+>>> data V = V
+>>> V
+No instance for (Show V)
+-}
+evals :: TEnv -> DynFlags -> [Statement] -> Ghc [Text]
+evals (st, fp) df stmts = do
+    er <- gStrictTry $ mapM eval stmts
+    return $ case er of
+        Left err -> errorLines err
+        Right rs -> concat . catMaybes $ rs
+  where
+    dbg = logWith st
+    eval :: Statement -> Ghc (Maybe [Text])
+    eval (Located l stmt)
+        | -- GHCi flags
+          Just (words -> flags) <- parseSetFlags stmt = do
+            dbg "{:SET" flags
+            ndf <- getInteractiveDynFlags
+            dbg "pre set" $ showDynFlags ndf
+            eans <-
+                liftIO $ try @GhcException $
+                parseDynamicFlagsCmdLine ndf
+                (map (L $ UnhelpfulSpan "<interactive>") flags)
+            dbg "parsed flags" $ eans
+              <&> (_1 %~ showDynFlags >>> _3 %~ map warnMsg)
+            case eans of
+                Left err -> pure $ Just $ errorLines $ show err
+                Right (df', ignoreds, warns) -> do
+                    let warnings = do
+                            guard $ not $ null warns
+                            pure $ errorLines $
+                                unlines $
+                                map prettyWarn warns
+                        igns = do
+                            guard $ not $ null ignoreds
+                            pure
+                                ["Some flags have not been recognized: "
+                                <> T.pack (intercalate ", " $ map SrcLoc.unLoc ignoreds)
+                                ]
+                    dbg "post set" $ showDynFlags df'
+                    _ <- setSessionDynFlags df'
+                    sessDyns <- getSessionDynFlags
+                    setInteractiveDynFlags sessDyns
+                    pure $ warnings <> igns
+        | -- A type/kind command
+          Just (cmd, arg) <- parseGhciLikeCmd $ T.pack stmt =
+            evalGhciLikeCmd cmd arg
+        | -- A statement
+          isStmt df stmt =
+            do
+                dbg "{STMT " stmt
+                res <- exec stmt l
+                r <- case res of
+                    Left err -> return . Just . errorLines $ err
+                    Right x  -> return $ singleLine <$> x
+                dbg "STMT} -> " r
+                return r
+        | -- An import
+          isImport df stmt =
+            do
+                dbg "{IMPORT " stmt
+                _ <- addImport stmt
+                return Nothing
+        | -- A declaration
+          otherwise =
+            do
+                dbg "{DECL " stmt
+                void $ runDecls stmt
+                return Nothing
+    exec stmt l =
+        let opts = execOptions{execSourceFile = fp, execLineNumber = l}
+         in myExecStmt stmt opts
+
+prettyWarn :: Warn -> String
+prettyWarn Warn{..} =
+    prettyPrint (SrcLoc.getLoc warnMsg) <> ": warning:\n"
+    <> "    " <> SrcLoc.unLoc warnMsg
+
+ghcSessionDepsDefinition :: HscEnvEq -> NormalizedFilePath -> Action HscEnv
+ghcSessionDepsDefinition env file = do
+        let hsc = hscEnvWithImportPaths env
+        deps <- use_ GetDependencies file
+        let tdeps = transitiveModuleDeps deps
+        ifaces <- uses_ GetModIface tdeps
+
+        -- Currently GetDependencies returns things in topological order so A comes before B if A imports B.
+        -- We need to reverse this as GHC gets very unhappy otherwise and complains about broken interfaces.
+        -- Long-term we might just want to change the order returned by GetDependencies
+        let inLoadOrder = reverse (map hirHomeMod ifaces)
+
+        liftIO $ loadModulesHome inLoadOrder <$> setupFinderCache (map hirModSummary ifaces) hsc
+
+runGetSession :: MonadIO m => IdeState -> NormalizedFilePath -> m HscEnv
+runGetSession st nfp = liftIO $ runAction "eval" st $ do
+    -- Create a new GHC Session rather than reusing an existing one
+    -- to avoid interfering with ghcide
+    IdeGhcSession{loadSessionFun} <- useNoFile_ GhcSessionIO
+    let fp = fromNormalizedFilePath nfp
+    ((_, res),_) <- liftIO $ loadSessionFun fp
+    let hscEnv = fromMaybe (error $ "Unknown file: " <> fp) res
+    ghcSessionDepsDefinition hscEnv nfp
+
+needsQuickCheck :: [(Section, Test)] -> Bool
+needsQuickCheck = any (isProperty . snd)
+
+hasQuickCheck :: DynFlags -> Bool
+hasQuickCheck df = hasPackage df "QuickCheck"
+
+singleLine :: String -> [Text]
+singleLine s = [T.pack s]
+
+{- |
+ Convert error messages to a list of text lines
+ Remove unnecessary information.
+-}
+errorLines :: String -> [Text]
+errorLines =
+        dropWhileEnd T.null
+        . takeWhile (not . ("CallStack" `T.isPrefixOf`))
+        . T.lines
+        . T.pack
+
+{- |
+>>> map (pad_ (T.pack "--")) (map T.pack ["2+2",""])
+["--2+2","--<BLANKLINE>"]
+-}
+pad_ :: Text -> Text -> Text
+pad_ prefix = (prefix `T.append`) . convertBlank
+
+convertBlank :: Text -> Text
+convertBlank x
+    | T.null x = "<BLANKLINE>"
+    | otherwise = x
+
+padPrefix :: IsString p => Format -> p
+padPrefix SingleLine = "-- "
+padPrefix _          = ""
+
+{- | Resulting @Text@ MUST NOT prefix each line with @--@
+   Such comment-related post-process will be taken place
+   solely in 'evalGhciLikeCmd'.
+-}
+type GHCiLikeCmd = DynFlags -> Text -> Ghc (Maybe Text)
+
+-- Should we use some sort of trie here?
+ghciLikeCommands :: [(Text, GHCiLikeCmd)]
+ghciLikeCommands =
+    [("kind", doKindCmd False), ("kind!", doKindCmd True), ("type", doTypeCmd)]
+
+evalGhciLikeCmd :: Text -> Text -> Ghc (Maybe [Text])
+evalGhciLikeCmd cmd arg = do
+    df <- getSessionDynFlags
+    case lookup cmd ghciLikeCommands
+        <|> snd
+        <$> find (T.isPrefixOf cmd . fst) ghciLikeCommands of
+        Just hndler ->
+            fmap
+                T.lines
+                <$> hndler df arg
+        _ -> E.throw $ GhciLikeCmdNotImplemented cmd arg
+
+doKindCmd :: Bool -> DynFlags -> Text -> Ghc (Maybe Text)
+doKindCmd False df arg = do
+    let input = T.strip arg
+    (_, kind) <- typeKind False $ T.unpack input
+    let kindText = text (T.unpack input) <+> "::" <+> ppr kind
+    pure $ Just $ T.pack (showSDoc df kindText)
+doKindCmd True df arg = do
+    let input = T.strip arg
+    (ty, kind) <- typeKind True $ T.unpack input
+    let kindDoc = text (T.unpack input) <+> "::" <+> ppr kind
+        tyDoc = "=" <+> ppr ty
+    pure $ Just $ T.pack (showSDoc df $ kindDoc $$ tyDoc)
+
+doTypeCmd :: DynFlags -> Text -> Ghc (Maybe Text)
+doTypeCmd dflags arg = do
+    let (emod, expr) = parseExprMode arg
+    ty <- exprType emod $ T.unpack expr
+    let rawType = T.strip $ T.pack $ showSDoc dflags $ ppr ty
+        broken = T.any (\c -> c == '\r' || c == '\n') rawType
+    pure $
+        Just $
+            if broken
+                then
+                    T.pack $
+                        showSDoc dflags $
+                            text (T.unpack expr)
+                                $$ nest 2 ("::" <+> ppr ty)
+                else expr <> " :: " <> rawType <> "\n"
+
+parseExprMode :: Text -> (TcRnExprMode, T.Text)
+parseExprMode rawArg = case T.break isSpace rawArg of
+    ("+v", rest) -> (TM_NoInst, T.strip rest)
+    ("+d", rest) -> (TM_Default, T.strip rest)
+    _            -> (TM_Inst, rawArg)
+
+data GhciLikeCmdException = GhciLikeCmdNotImplemented
+    { ghciCmdName :: Text
+    , ghciCmdArg  :: Text
+    }
+    deriving (Typeable)
+
+instance Show GhciLikeCmdException where
+    showsPrec _ GhciLikeCmdNotImplemented{..} =
+        showString "unknown command '"
+            . showString (T.unpack ghciCmdName)
+            . showChar '\''
+
+instance E.Exception GhciLikeCmdException
+
+{-
+>>> parseGhciLikeCmd (T.pack ":kind! N + M + 1")
+Just ("kind!","N + M + 1")
+>>> parseGhciLikeCmd (T.pack ":kind a")
+Just ("kind","a")
+-}
+parseGhciLikeCmd :: Text -> Maybe (Text, Text)
+parseGhciLikeCmd input = do
+    (':', rest) <- T.uncons $ T.stripStart input
+    pure $ second T.strip $ T.break isSpace rest
+
+setupDynFlagsForGHCiLike :: HscEnv -> DynFlags -> IO DynFlags
+setupDynFlagsForGHCiLike env dflags = do
+    let dflags3 =
+            dflags
+                { hscTarget = HscInterpreted
+                , ghcMode = CompManager
+                , ghcLink = LinkInMemory
+                }
+        platform = targetPlatform dflags3
+        dflags3a = updateWays $ dflags3{ways = interpWays}
+        dflags3b =
+            foldl gopt_set dflags3a $
+                concatMap (wayGeneralFlags platform) interpWays
+        dflags3c =
+            foldl gopt_unset dflags3b $
+                concatMap (wayUnsetGeneralFlags platform) interpWays
+        dflags4 =
+            dflags3c
+                `gopt_set` Opt_ImplicitImportQualified
+                `gopt_set` Opt_IgnoreOptimChanges
+                `gopt_set` Opt_IgnoreHpcChanges
+                `gopt_unset` Opt_DiagnosticsShowCaret
+    initializePlugins env dflags4
src/Ide/Plugin/Eval/GHC.hs view
@@ -1,206 +1,174 @@-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE StandaloneDeriving #-}-{-# OPTIONS_GHC -fno-warn-unused-imports -Wno-orphans #-}---- |GHC API utilities-module Ide.Plugin.Eval.GHC (-    isExpr,-    addExtension,-    addImport,-    hasPackage,-    addPackages,-    modifyFlags,-    showDynFlags,-) where--import Data.List (isPrefixOf)-import Data.Maybe (mapMaybe)-import Development.IDE.GHC.Compat-import qualified EnumSet-import GHC.LanguageExtensions.Type (Extension (..))-import GhcMonad (modifySession)-import GhcPlugins (DefUnitId (..), InstalledUnitId (..), fsLit, hsc_IC, pprHsString)-import HscTypes (InteractiveContext (ic_dflags))-import Ide.Plugin.Eval.Util (asS, gStrictTry)-import qualified Lexer-import Module (UnitId (DefiniteUnitId))-import Outputable (-    Outputable (ppr),-    SDoc,-    showSDocUnsafe,-    text,-    vcat,-    (<+>),- )-import qualified Parser-import SrcLoc (mkRealSrcLoc)-import StringBuffer (stringToStringBuffer)-import Data.String (fromString)--{- $setup->>> import GHC->>> import GHC.Paths->>> run act = runGhc (Just libdir) (getInteractiveDynFlags >>= act)->>> libdir-"/Users/titto/.ghcup/ghc/8.8.4/lib/ghc-8.8.4"--}--{- | Returns true if string is an expression-->>> isExprTst e df = return (isExpr df e)->>> run $ isExprTst "3"-True-->>> run $ isExprTst "(x+y)"-True-->>> run $ isExprTst "import Data.Maybe"-False-->>> run $ isExprTst "three=3"-False--}-isExpr :: DynFlags -> String -> Bool-isExpr df stmt = case parseThing Parser.parseExpression df stmt of-    Lexer.POk _ _ -> True-    Lexer.PFailed{} -> False--parseThing :: Lexer.P thing -> DynFlags -> String -> Lexer.ParseResult thing-parseThing parser dflags stmt = do-    let buf = stringToStringBuffer stmt-        loc = mkRealSrcLoc (fsLit "<interactive>") 1 1--    Lexer.unP parser (Lexer.mkPState dflags buf loc)--{- | True if specified package is present in DynFlags---- >>> hasPackageTst pkg = run $ \df -> return (hasPackage df pkg)->>> hasPackageTst pkg = run $ \_ -> addPackages [pkg] >>= return . either Left (\df -> Right (hasPackage df pkg))-->>> hasPackageTst "base"-Right True-->>> hasPackageTst "ghc"-Right True-->>> hasPackageTst "extra"-Left "<command line>: cannot satisfy -package extra\n    (use -v for more information)"-->>> hasPackageTst "QuickCheck"-Left "<command line>: cannot satisfy -package QuickCheck\n    (use -v for more information)"--}-hasPackage :: DynFlags -> String -> Bool-hasPackage df = hasPackage_ (packageFlags df)--hasPackage_ :: [PackageFlag] -> [Char] -> Bool-hasPackage_ pkgFlags name = any (name `isPrefixOf`) (pkgNames_ pkgFlags)--{- |->>> run (return . pkgNames)-[]--}-pkgNames :: DynFlags -> [String]-pkgNames = pkgNames_ . packageFlags--pkgNames_ :: [PackageFlag] -> [String]-pkgNames_ =-    mapMaybe-        ( \case-            ExposePackage _ (PackageArg n) _ -> Just n-            ExposePackage _ (UnitIdArg (DefiniteUnitId n)) _ -> Just $ asS n-            _ -> Nothing-        )--{- | Expose a list of packages.->>> addPackagesTest pkgs = run (\_ -> (packageFlags <$>) <$> addPackages pkgs)-->>> addPackagesTest []-Right []-->>> addPackagesTest ["base","base","array"]-Right [-package base{package base True ([])},-package array{package array True ([])}]-->>> addPackagesTest ["Cabal"]-Right [-package Cabal{package Cabal True ([])}]-->>> addPackagesTest ["QuickCheck"]-Left "<command line>: cannot satisfy -package QuickCheck\n    (use -v for more information)"-->>> addPackagesTest ["base","notThere"]-Left "<command line>: cannot satisfy -package notThere\n    (use -v for more information)"--prop> \(x::Int) -> x + x == 2 * x-+++ OK, passed 100 tests.--}-addPackages :: [String] -> Ghc (Either String DynFlags)-addPackages pkgNames = gStrictTry $-    modifyFlags $ \df ->-        df{packageFlags = foldr (\pkgName pf -> if hasPackage_ pf pkgName then pf else expose pkgName : pf) (packageFlags df) pkgNames}-  where-    expose name = ExposePackage ("-package " ++ name) (PackageArg name) (ModRenaming True [])--modifyFlags :: GhcMonad m => (DynFlags -> DynFlags) -> m DynFlags-modifyFlags f = do-    df <- getSessionDynFlags-    _ <- setSessionDynFlags (f df)-    getSessionDynFlags---- modifyFlags f = do---         modifyDynFlags f---         getSessionDynFlags--{- | Add import to evaluation context-->>> run $ \_ -> addImport "import Data.Maybe"-Could not find module ‘Data.Maybe’-Use -v (or `:set -v` in ghci) to see a list of the files searched for.-->>> run $ \df -> addPackages ["base"] >> addImport "import Data.Maybe"-[import Data.Maybe]-->>> run $ \df -> addPackages ["base"] >> addImport "import qualified Data.Maybe as M"-[import qualified Data.Maybe as M]--}-addImport :: GhcMonad m => String -> m [InteractiveImport]-addImport i = do-    ctx <- getContext-    -- dbgO "CONTEXT" ctx-    idecl <- parseImportDecl i-    setContext $ IIDecl idecl : ctx-    -- ctx' <- getContext-    -- dbg "CONTEXT'" ctx'-    getContext--{- | Add extension to interactive evaluation session->>> import GHC.LanguageExtensions.Type(Extension(..))->>> run $ \_ -> addExtension DeriveGeneric-()--}-addExtension :: GhcMonad m => Extension -> m ()-addExtension ext =-    modifySession $ \hsc -> hsc{hsc_IC = setExtension (hsc_IC hsc) ext}--setExtension :: InteractiveContext -> Extension -> InteractiveContext-setExtension ic ext = ic{ic_dflags = xopt_set (ic_dflags ic) ext}--deriving instance Read Extension---- Partial display of DynFlags contents, for testing purposes-showDynFlags :: DynFlags -> String-showDynFlags df =-    showSDocUnsafe . vcat . map (\(n, d) -> text (n ++ ": ") <+> d) $-        [ ("extensions", ppr . extensions $ df)-        , ("extensionFlags", ppr . EnumSet.toList . extensionFlags $ df)-        , ("importPaths", vList $ importPaths df)-        , ("generalFlags", pprHsString . fromString . show . EnumSet.toList . generalFlags $ df)-        , -- , ("includePaths", text . show $ includePaths df)-          -- ("packageEnv", ppr $ packageEnv df)-          ("pkgNames", vcat . map text $ pkgNames df)-        , ("packageFlags", vcat . map ppr $ packageFlags df)-        -- ,("pkgDatabase",(map) (ppr . installedPackageId) . pkgDatabase $ df)-        -- ("pkgDatabase", text . show <$> pkgDatabase $ df)-        ]--vList :: [String] -> SDoc-vList = vcat . map text+{-# LANGUAGE LambdaCase          #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE StandaloneDeriving  #-}
+{-# OPTIONS_GHC -fno-warn-unused-imports -Wno-orphans #-}
+
+-- |GHC API utilities
+module Ide.Plugin.Eval.GHC (
+    addExtension,
+    addImport,
+    hasPackage,
+    addPackages,
+    modifyFlags,
+    showDynFlags,
+) where
+
+import           Data.List                   (isPrefixOf)
+import           Data.Maybe                  (mapMaybe)
+import           Data.String                 (fromString)
+import           Development.IDE.GHC.Compat
+import qualified EnumSet
+import           GHC.LanguageExtensions.Type (Extension (..))
+import           GhcMonad                    (modifySession)
+import           GhcPlugins                  (DefUnitId (..),
+                                              InstalledUnitId (..), fsLit,
+                                              hsc_IC, pprHsString)
+import           HscTypes                    (InteractiveContext (ic_dflags))
+import           Ide.Plugin.Eval.Util        (asS, gStrictTry)
+import qualified Lexer
+import           Module                      (UnitId (DefiniteUnitId))
+import           Outputable                  (Outputable (ppr), SDoc,
+                                              showSDocUnsafe, text, vcat, (<+>))
+import qualified Parser
+import           SrcLoc                      (mkRealSrcLoc)
+import           StringBuffer                (stringToStringBuffer)
+
+{- $setup
+>>> import GHC
+>>> import GHC.Paths
+>>> run act = runGhc (Just libdir) (getInteractiveDynFlags >>= act)
+>>> libdir
+"/Users/titto/.ghcup/ghc/8.8.4/lib/ghc-8.8.4"
+-}
+
+{- | True if specified package is present in DynFlags
+
+-- >>> hasPackageTst pkg = run $ \df -> return (hasPackage df pkg)
+>>> hasPackageTst pkg = run $ \_ -> addPackages [pkg] >>= return . either Left (\df -> Right (hasPackage df pkg))
+
+>>> hasPackageTst "base"
+Right True
+
+>>> hasPackageTst "ghc"
+Right True
+
+>>> hasPackageTst "extra"
+Left "<command line>: cannot satisfy -package extra\n    (use -v for more information)"
+
+>>> hasPackageTst "QuickCheck"
+Left "<command line>: cannot satisfy -package QuickCheck\n    (use -v for more information)"
+-}
+hasPackage :: DynFlags -> String -> Bool
+hasPackage df = hasPackage_ (packageFlags df)
+
+hasPackage_ :: [PackageFlag] -> [Char] -> Bool
+hasPackage_ pkgFlags name = any (name `isPrefixOf`) (pkgNames_ pkgFlags)
+
+{- |
+>>> run (return . pkgNames)
+[]
+-}
+pkgNames :: DynFlags -> [String]
+pkgNames = pkgNames_ . packageFlags
+
+pkgNames_ :: [PackageFlag] -> [String]
+pkgNames_ =
+    mapMaybe
+        ( \case
+            ExposePackage _ (PackageArg n) _                 -> Just n
+            ExposePackage _ (UnitIdArg (DefiniteUnitId n)) _ -> Just $ asS n
+            _                                                -> Nothing
+        )
+
+{- | Expose a list of packages.
+>>> addPackagesTest pkgs = run (\_ -> (packageFlags <$>) <$> addPackages pkgs)
+
+>>> addPackagesTest []
+Right []
+
+>>> addPackagesTest ["base","base","array"]
+Right [-package base{package base True ([])},-package array{package array True ([])}]
+
+>>> addPackagesTest ["Cabal"]
+Right [-package Cabal{package Cabal True ([])}]
+
+>>> addPackagesTest ["QuickCheck"]
+Left "<command line>: cannot satisfy -package QuickCheck\n    (use -v for more information)"
+
+>>> addPackagesTest ["base","notThere"]
+Left "<command line>: cannot satisfy -package notThere\n    (use -v for more information)"
+
+prop> \(x::Int) -> x + x == 2 * x
++++ OK, passed 100 tests.
+-}
+addPackages :: [String] -> Ghc (Either String DynFlags)
+addPackages pkgNames = gStrictTry $
+    modifyFlags $ \df ->
+        df{packageFlags = foldr (\pkgName pf -> if hasPackage_ pf pkgName then pf else expose pkgName : pf) (packageFlags df) pkgNames}
+  where
+    expose name = ExposePackage ("-package " ++ name) (PackageArg name) (ModRenaming True [])
+
+modifyFlags :: GhcMonad m => (DynFlags -> DynFlags) -> m DynFlags
+modifyFlags f = do
+    df <- getSessionDynFlags
+    _ <- setSessionDynFlags (f df)
+    getSessionDynFlags
+
+-- modifyFlags f = do
+--         modifyDynFlags f
+--         getSessionDynFlags
+
+{- | Add import to evaluation context
+
+>>> run $ \_ -> addImport "import Data.Maybe"
+Could not find module ‘Data.Maybe’
+Use -v (or `:set -v` in ghci) to see a list of the files searched for.
+
+>>> run $ \df -> addPackages ["base"] >> addImport "import Data.Maybe"
+[import Data.Maybe]
+
+>>> run $ \df -> addPackages ["base"] >> addImport "import qualified Data.Maybe as M"
+[import qualified Data.Maybe as M]
+-}
+addImport :: GhcMonad m => String -> m [InteractiveImport]
+addImport i = do
+    ctx <- getContext
+    -- dbgO "CONTEXT" ctx
+    idecl <- parseImportDecl i
+    setContext $ IIDecl idecl : ctx
+    -- ctx' <- getContext
+    -- dbg "CONTEXT'" ctx'
+    getContext
+
+{- | Add extension to interactive evaluation session
+>>> import GHC.LanguageExtensions.Type(Extension(..))
+>>> run $ \_ -> addExtension DeriveGeneric
+()
+-}
+addExtension :: GhcMonad m => Extension -> m ()
+addExtension ext =
+    modifySession $ \hsc -> hsc{hsc_IC = setExtension (hsc_IC hsc) ext}
+
+setExtension :: InteractiveContext -> Extension -> InteractiveContext
+setExtension ic ext = ic{ic_dflags = xopt_set (ic_dflags ic) ext}
+
+deriving instance Read Extension
+
+-- Partial display of DynFlags contents, for testing purposes
+showDynFlags :: DynFlags -> String
+showDynFlags df =
+    showSDocUnsafe . vcat . map (\(n, d) -> text (n ++ ": ") <+> d) $
+        [ ("extensions", ppr . extensions $ df)
+        , ("extensionFlags", ppr . EnumSet.toList . extensionFlags $ df)
+        , ("importPaths", vList $ importPaths df)
+        , ("generalFlags", pprHsString . fromString . show . EnumSet.toList . generalFlags $ df)
+        , -- , ("includePaths", text . show $ includePaths df)
+          -- ("packageEnv", ppr $ packageEnv df)
+          ("pkgNames", vcat . map text $ pkgNames df)
+        , ("packageFlags", vcat . map ppr $ packageFlags df)
+        -- ,("pkgDatabase",(map) (ppr . installedPackageId) . pkgDatabase $ df)
+        -- ("pkgDatabase", text . show <$> pkgDatabase $ df)
+        ]
+
+vList :: [String] -> SDoc
+vList = vcat . map text
src/Ide/Plugin/Eval/Parse/Comments.hs view
@@ -1,575 +1,571 @@-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE DerivingStrategies #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE RecordWildCards #-}-{-# LANGUAGE TemplateHaskell #-}-{-# LANGUAGE TupleSections #-}--module Ide.Plugin.Eval.Parse.Comments where--import qualified Control.Applicative.Combinators.NonEmpty as NE-import Control.Arrow (first, (&&&), (>>>))-import Control.Lens (lensField, lensRules, view, (.~), (^.))-import Control.Lens.Extras (is)-import Control.Lens.TH (makeLensesWith, makePrisms, mappingNamer)-import Control.Monad (guard, void, when)-import Control.Monad.Combinators ()-import Control.Monad.Reader (ask)-import Control.Monad.Trans.Reader (Reader, runReader)-import qualified Data.Char as C-import qualified Data.DList as DL-import qualified Data.Foldable as F-import Data.Function ((&))-import Data.Functor.Identity-import Data.List.NonEmpty (NonEmpty ((:|)))-import qualified Data.List.NonEmpty as NE-import Data.Map.Strict (Map)-import qualified Data.Map.Strict as Map-import Data.Void (Void)-import Development.IDE (Position, Range (Range))-import Development.IDE.Types.Location (Position (..))-import GHC.Generics-import Ide.Plugin.Eval.Types-import Language.LSP.Types.Lens-    ( character,-      end,-      line,-      start,-    )-import Text.Megaparsec-import qualified Text.Megaparsec as P-import Text.Megaparsec.Char-    ( alphaNumChar,-      char,-      eol,-      hspace,-      letterChar,-    )-import Data.Functor ((<&>))-import qualified Data.Text as T--{--We build parsers combining the following three kinds of them:--    *   Line parser - paring a single line into an input,-        works both for line- and block-comments.-        A line should be a proper content of lines contained in comment:-        doesn't include starting @--@ and @{\-@ and no ending @-\}@--    *   Line comment group parser: parses a contiguous group of-        tuples of position and line comment into sections of line comments.-        Each input MUST start with @--@.--    *   Block comment parser: Parsing entire block comment into sections.-        Input must be surrounded by @{\-@ and @-\}@.--}---- | Line parser-type LineParser a = forall m. Monad m => ParsecT Void String m a---- | Line comment group parser-type LineGroupParser = Parsec Void [(Range, RawLineComment)]--data BlockEnv = BlockEnv-    { isLhs :: Bool-    , blockRange :: Range-    }-    deriving (Read, Show, Eq, Ord)--makeLensesWith-    (lensRules & lensField .~ mappingNamer (pure . (++ "L")))-    ''BlockEnv---- | Block comment parser-type BlockCommentParser = ParsecT Void String (Reader BlockEnv)---- | Prop line, with "prop>" stripped off-newtype PropLine = PropLine {getPropLine :: String}-    deriving (Show)---- | Example line, with @>>>@ stripped off-newtype ExampleLine = ExampleLine {getExampleLine :: String}-    deriving (Show)--data TestComment-    = AProp-        { testCommentRange :: Range-        , lineProp :: PropLine-        , propResults :: [String]-        }-    | AnExample-        { testCommentRange :: Range-        , lineExamples :: NonEmpty ExampleLine-        , exampleResults :: [String]-        }-    deriving (Show)---- | Classification of comments-data CommentFlavour = Vanilla | HaddockNext | HaddockPrev | Named String-    deriving (Read, Show, Eq, Ord)---- | Single line or block comments?-data CommentStyle = Line | Block Range-    deriving (Read, Show, Eq, Ord, Generic)--makePrisms ''CommentStyle--commentsToSections ::-    -- | True if it is literate Haskell-    Bool ->-    Comments ->-    Sections-commentsToSections isLHS Comments {..} =-    let (lineSectionSeeds, lineSetupSeeds) =-            foldMap-                ( \lcs ->-                    let theRan =-                            Range-                                (view start $ fst $ NE.head lcs)-                                (view end $ fst $ NE.last lcs)-                     in case parseMaybe lineGroupP $ NE.toList lcs of-                            Nothing -> mempty-                            Just (mls, rs) ->-                                ( maybe mempty (uncurry Map.singleton) ((theRan,) <$> mls)-                                , -- orders setup sections in ascending order-                                  if null rs-                                    then mempty-                                    else-                                        Map.singleton theRan $-                                            DL.singleton (Line, rs)-                                )-                )-                $ groupLineComments $-                    Map.filterWithKey-                        -- FIXME:-                        -- To comply with the initial behaviour of-                        -- Extended Eval Plugin;-                        -- but it also rejects modules with-                        -- non-zero base indentation level!-                        ( \pos _ ->-                            if isLHS-                                then pos ^. start . character == 2-                                else pos ^. start . character == 0-                        )-                        lineComments-        (blockSeed, blockSetupSeeds) =-            foldMap-                ( \(ran, lcs) ->-                    case parseBlockMaybe isLHS ran blockCommentBP $-                        getRawBlockComment lcs of-                        Nothing -> mempty-                        Just (Named "setup", grp) ->-                            -- orders setup sections in ascending order-                            ( mempty-                            , Map.singleton ran $-                                DL.singleton (Block ran, grp)-                            )-                        Just grp ->-                            ( Map.singleton ran grp-                            , mempty-                            )-                )-                -- It seems Extended Eval Plugin doesn't constraint-                -- starting indentation level for block comments.-                -- Rather, it constrains the indentation level /inside/-                -- block comment body.-                $ Map.toList blockComments-        lineSections =-            lineSectionSeeds <&> uncurry (testsToSection Line)-        multilineSections =-            Map.mapWithKey-                (uncurry . testsToSection . Block)-                blockSeed-        setupSections =-            -- Setups doesn't need Dummy position-            map-                ( \(style, tests) ->-                    testsToSection-                        style-                        (Named "setup")-                        tests-                )-                $ DL.toList $-                    F.fold $-                        Map.unionWith (<>) lineSetupSeeds blockSetupSeeds-        nonSetupSections = F.toList $ lineSections `Map.union` multilineSections-     in Sections {..}--parseBlockMaybe :: Bool -> Range -> BlockCommentParser a -> String -> Maybe a-parseBlockMaybe isLhs blockRange p i =-    case runReader (runParserT p' "" i) BlockEnv {..} of-        Left {} -> Nothing-        Right a -> Just a-    where-        p' = do-            updateParserState $ \st ->-                st-                    { statePosState =-                        (statePosState st)-                            { pstateSourcePos = positionToSourcePos $ blockRange ^. start-                            }-                    }-            p--type CommentRange = Range--type SectionRange = Range--testsToSection ::-    CommentStyle ->-    CommentFlavour ->-    [TestComment] ->-    Section-testsToSection style flav tests =-    let sectionName-            | Named name <- flav = name-            | otherwise = ""-        sectionLanguage = case flav of-            HaddockNext -> Haddock-            HaddockPrev -> Haddock-            _ -> Plain-        sectionTests = map fromTestComment tests-        sectionFormat =-            case style of-                Line -> SingleLine-                Block ran -> MultiLine ran-     in Section {..}--fromTestComment :: TestComment -> Test-fromTestComment AProp {..} =-    Property-        { testline = getPropLine lineProp-        , testOutput = propResults-        , testRange = testCommentRange-        }-fromTestComment AnExample {..} =-    Example-        { testLines = getExampleLine <$> lineExamples-        , testOutput = exampleResults-        , testRange = testCommentRange-        }---- * Block comment parser--{- $setup->>> dummyPos = Position 0 0->>> parseE p = either (error . errorBundlePretty) id . parse p ""--}---- >>> parseE (blockCommentBP True dummyPos) "{- |\n  >>> 5+5\n  11\n  -}"--- (HaddockNext,[AnExample {testCommentRange = Position {_line = 1, _character = 0}, lineExamples = ExampleLine {getExampleLine = " 5+5"} :| [], exampleResults = ["  11"]}])--blockCommentBP ::-    -- | True if Literate Haskell-    BlockCommentParser (CommentFlavour, [TestComment])-blockCommentBP = do-    skipCount 2 anySingle -- "{-"-    void $ optional $ char ' '-    flav <- commentFlavourP-    hit <- skipNormalCommentBlock-    if hit-        then do-            body <--                many $-                    (blockExamples <|> blockProp)-                        <* skipNormalCommentBlock-            void takeRest -- just consume the rest-            pure (flav, body)-        else pure (flav, [])--skipNormalCommentBlock :: BlockCommentParser Bool-skipNormalCommentBlock = do-    BlockEnv {..} <- ask-    skipManyTill (normalLineP isLhs $ Block blockRange) $-        False <$ try (optional (chunk "-}") *> eof)-            <|> True <$ lookAhead (try $ testSymbol isLhs $ Block blockRange)--testSymbol :: Bool -> CommentStyle -> LineParser ()-testSymbol isLHS style =-    -- FIXME: To comply with existing Extended Eval Plugin Behaviour;-    -- it must skip one space after a comment!-    -- This prevents Eval Plugin from working on-    -- modules with non-standard base indentation-level.-    when (isLHS && is _Block style) (void $ count' 0 2 $ char ' ')-        *> (exampleSymbol <|> propSymbol)--eob :: LineParser ()-eob = eof <|> try (optional (chunk "-}") *> eof) <|> void eol--blockExamples-    , blockProp ::-        BlockCommentParser TestComment-blockExamples = do-    BlockEnv {..} <- ask-    (ran, examples) <- withRange $ NE.some $ exampleLineStrP isLhs $ Block blockRange-    AnExample ran examples <$> resultBlockP-blockProp = do-    BlockEnv {..} <- ask-    (ran, Identity prop) <- withRange $ fmap Identity $ propLineStrP isLhs $ Block blockRange-    AProp ran prop <$> resultBlockP--withRange ::-    (TraversableStream s, Stream s, Monad m, Ord v, Traversable t) =>-    ParsecT v s m (t (a, Position)) ->-    ParsecT v s m (Range, t a)-withRange p = do-    beg <- sourcePosToPosition <$> getSourcePos-    as <- p-    let fin-            | null as = beg-            | otherwise = snd $ last $ F.toList as-    pure (Range beg fin, fst <$> as)--resultBlockP :: BlockCommentParser [String]-resultBlockP = do-    BlockEnv {..} <- ask-    many $-        fmap fst . nonEmptyNormalLineP isLhs $-            Block blockRange--positionToSourcePos :: Position -> SourcePos-positionToSourcePos pos =-    P.SourcePos-        { sourceName = "<block comment>"-        , sourceLine = P.mkPos $ 1 + pos ^. line-        , sourceColumn = P.mkPos $ 1 + pos ^. character-        }--sourcePosToPosition :: SourcePos -> Position-sourcePosToPosition SourcePos {..} =-    Position (unPos sourceLine - 1) (unPos sourceColumn - 1)---- * Line Group Parser--{- |-Result: a tuple of ordinary line tests and setting sections.--TODO: Haddock comment can adjacent to vanilla comment:--    @-        -- Vanilla comment-        -- Another vanilla-        -- | This parses as Haddock comment as GHC-    @--This behaviour is not yet handled correctly in Eval Plugin;-but for future extension for this, we use a tuple here instead of 'Either'.--}-lineGroupP ::-    LineGroupParser-        (Maybe (CommentFlavour, [TestComment]), [TestComment])-lineGroupP = do-    (_, flav) <- lookAhead $ parseLine (commentFlavourP <* takeRest)-    case flav of-        Named "setup" -> (Nothing,) <$> lineCommentSectionsP-        flav -> (,mempty) . Just . (flav,) <$> lineCommentSectionsP---- >>>  parse (lineGroupP <*eof) "" $ (dummyPosition, ) . RawLineComment <$> ["-- a", "-- b"]--- Variable not in scope: dummyPosition :: Position--commentFlavourP :: LineParser CommentFlavour-commentFlavourP =-    P.option-        Vanilla-        ( HaddockNext <$ char '|'-            <|> HaddockPrev <$ char '^'-            <|> Named <$ char '$'-                <* optional hspace-                <*> ((:) <$> letterChar <*> P.many alphaNumChar)-        )-        <* optional (char ' ')--lineCommentHeadP :: LineParser ()-lineCommentHeadP = do-    -- and no operator symbol character follows.-    void $ chunk "--"-    skipMany $ char '-'-    void $ optional $ char ' '--lineCommentSectionsP ::-    LineGroupParser [TestComment]-lineCommentSectionsP = do-    skipMany normalLineCommentP-    many $-        exampleLinesGP-            <|> uncurry AProp <$> propLineGP <*> resultLinesP-                <* skipMany normalLineCommentP--lexemeLine :: LineGroupParser a -> LineGroupParser a-lexemeLine p = p <* skipMany normalLineCommentP--resultLinesP :: LineGroupParser [String]-resultLinesP = many nonEmptyLGP--normalLineCommentP :: LineGroupParser (Range, String)-normalLineCommentP =-    parseLine (fst <$ commentFlavourP <*> normalLineP False Line)--nonEmptyLGP :: LineGroupParser String-nonEmptyLGP =-    try $-        fmap snd $-            parseLine $-                fst <$ commentFlavourP <*> nonEmptyNormalLineP False Line--exampleLinesGP :: LineGroupParser TestComment-exampleLinesGP =-    lexemeLine $-        uncurry AnExample . first convexHullRange . NE.unzip-            <$> NE.some exampleLineGP-            <*> resultLinesP--convexHullRange :: NonEmpty Range -> Range-convexHullRange nes =-    Range (NE.head nes ^. start) (NE.last nes ^. end)--exampleLineGP :: LineGroupParser (Range, ExampleLine)-exampleLineGP =-    -- In line-comments, indentation-level inside comment doesn't matter.-    parseLine (fst <$ commentFlavourP <*> exampleLineStrP False Line)--propLineGP :: LineGroupParser (Range, PropLine)-propLineGP =-    -- In line-comments, indentation-level inside comment doesn't matter.-    parseLine (fst <$ commentFlavourP <*> propLineStrP False Line)--{- |-Turning a line parser into line group parser consuming a single line comment.-Parses a sinlge line comment, skipping prefix "--[-*]" with optional one horizontal space.-fails if the input does not start with "--".--__N.B.__ We don't strip comment flavours.-->>> pck = (:[]).(:[]) . RawLineComment-->>> parseMaybe (parseLine $ takeRest) $ pck "-- >>> A"-Just [">>> A"]-->>> parseMaybe (parseLine $ takeRest) $ pck "---  >>> A"-Just [" >>> A"]-->>> parseMaybe (parseLine takeRest) $ pck ""-Nothing--}-parseLine ::-    (Ord (f RawLineComment), Traversable f) =>-    LineParser a ->-    Parsec Void [f RawLineComment] (f a)-parseLine p =-    P.token-        (mapM $ parseMaybe (lineCommentHeadP *> p) . getRawLineComment)-        mempty---- * Line Parsers---- | Non-empty normal line.-nonEmptyNormalLineP ::-    -- | True if Literate Haskell-    Bool ->-    CommentStyle ->-    LineParser (String, Position)-nonEmptyNormalLineP isLHS style = try $ do-    (ln, pos) <- normalLineP isLHS style-    guard $-        case style of-            Block{} -> T.strip (T.pack ln) `notElem` ["{-", "-}", ""]-            _ -> not $ all C.isSpace ln-    pure (ln, pos)--{- | Normal line is a line neither a example nor prop.- Empty line is normal.--}-normalLineP ::-    -- | True if Literate Haskell-    Bool ->-    CommentStyle ->-    LineParser (String, Position)-normalLineP isLHS style = do-    notFollowedBy-        (try $ testSymbol isLHS style)-    when (isLHS && is _Block style) $-        void $ count' 0 2 $ char ' '-    consume style--consume :: CommentStyle -> LineParser (String, Position)-consume style =-    case style of-        Line -> (,) <$> takeRest <*> getPosition-        Block {} -> manyTill_ anySingle (getPosition <* eob)--getPosition :: (Ord v, TraversableStream s) => ParsecT v s m Position-getPosition = sourcePosToPosition <$> getSourcePos---- | Parses example test line.-exampleLineStrP ::-    -- | True if Literate Haskell-    Bool ->-    CommentStyle ->-    LineParser (ExampleLine, Position)-exampleLineStrP isLHS style =-    try $-        -- FIXME: To comply with existing Extended Eval Plugin Behaviour;-        -- it must skip one space after a comment!-        -- This prevents Eval Plugin from working on-        -- modules with non-standard base indentation-level.-        when (isLHS && is _Block style) (void $ count' 0 2 $ char ' ')-            *> exampleSymbol-            *> (first ExampleLine <$> consume style)--exampleSymbol :: LineParser ()-exampleSymbol =-    chunk ">>>" *> P.notFollowedBy (char '>')--propSymbol :: LineParser ()-propSymbol = chunk "prop>" *> P.notFollowedBy (char '>')---- | Parses prop test line.-propLineStrP ::-    -- | True if Literate HAskell-    Bool ->-    CommentStyle ->-    LineParser (PropLine, Position)-propLineStrP isLHS style =-    -- FIXME: To comply with existing Extended Eval Plugin Behaviour;-    -- it must skip one space after a comment!-    -- This prevents Eval Plugin from working on-    -- modules with non-standard base indentation-level.-    when (isLHS && is _Block style) (void $ count' 0 2 $ char ' ')-        *> chunk "prop>"-        *> P.notFollowedBy (char '>')-        *> (first PropLine <$> consume style)---- * Utilities--{- |-Given a sequence of tokens increasing in their starting position,-groups them into sublists consisting of contiguous tokens;-Two adjacent tokens are considered to be contiguous if--    * line number increases by 1, and-    * they have same starting column.-->>> contiguousGroupOn id [(1,2),(2,2),(3,4),(4,4),(5,4),(7,0),(8,0)]-[(1,2) :| [(2,2)],(3,4) :| [(4,4),(5,4)],(7,0) :| [(8,0)]]--}-contiguousGroupOn :: (a -> (Int, Int)) -> [a] -> [NonEmpty a]-contiguousGroupOn toLineCol = foldr step []-    where-        step a [] = [pure a]-        step a bss0@((b :| bs) : bss)-            | let (aLine, aCol) = toLineCol a-              , let (bLine, bCol) = toLineCol b-              , aLine + 1 == bLine && aCol == bCol =-                (a :| b : bs) : bss-            | otherwise = pure a : bss0--{- | Given a map from positions, divides them into subgroup- with contiguous line and columns.--}-groupLineComments ::-    Map Range a -> [NonEmpty (Range, a)]-groupLineComments =-    contiguousGroupOn (fst >>> view start >>> view line &&& view character)-        . Map.toList+{-# LANGUAGE DeriveGeneric      #-}
+{-# LANGUAGE DerivingStrategies #-}
+{-# LANGUAGE FlexibleInstances  #-}
+{-# LANGUAGE OverloadedStrings  #-}
+{-# LANGUAGE RankNTypes         #-}
+{-# LANGUAGE RecordWildCards    #-}
+{-# LANGUAGE TemplateHaskell    #-}
+{-# LANGUAGE TupleSections      #-}
+
+module Ide.Plugin.Eval.Parse.Comments where
+
+import qualified Control.Applicative.Combinators.NonEmpty as NE
+import           Control.Arrow                            (first, (&&&), (>>>))
+import           Control.Lens                             (lensField, lensRules,
+                                                           view, (.~), (^.))
+import           Control.Lens.Extras                      (is)
+import           Control.Lens.TH                          (makeLensesWith,
+                                                           makePrisms,
+                                                           mappingNamer)
+import           Control.Monad                            (guard, void, when)
+import           Control.Monad.Combinators                ()
+import           Control.Monad.Reader                     (ask)
+import           Control.Monad.Trans.Reader               (Reader, runReader)
+import qualified Data.Char                                as C
+import qualified Data.DList                               as DL
+import qualified Data.Foldable                            as F
+import           Data.Function                            ((&))
+import           Data.Functor                             ((<&>))
+import           Data.Functor.Identity
+import           Data.List.NonEmpty                       (NonEmpty ((:|)))
+import qualified Data.List.NonEmpty                       as NE
+import           Data.Map.Strict                          (Map)
+import qualified Data.Map.Strict                          as Map
+import qualified Data.Text                                as T
+import           Data.Void                                (Void)
+import           Development.IDE                          (Position,
+                                                           Range (Range))
+import           Development.IDE.Types.Location           (Position (..))
+import           GHC.Generics
+import           Ide.Plugin.Eval.Types
+import           Language.LSP.Types.Lens                  (character, end, line,
+                                                           start)
+import           Text.Megaparsec
+import qualified Text.Megaparsec                          as P
+import           Text.Megaparsec.Char                     (alphaNumChar, char,
+                                                           eol, hspace,
+                                                           letterChar)
+
+{-
+We build parsers combining the following three kinds of them:
+
+    *   Line parser - paring a single line into an input,
+        works both for line- and block-comments.
+        A line should be a proper content of lines contained in comment:
+        doesn't include starting @--@ and @{\-@ and no ending @-\}@
+
+    *   Line comment group parser: parses a contiguous group of
+        tuples of position and line comment into sections of line comments.
+        Each input MUST start with @--@.
+
+    *   Block comment parser: Parsing entire block comment into sections.
+        Input must be surrounded by @{\-@ and @-\}@.
+-}
+
+-- | Line parser
+type LineParser a = forall m. Monad m => ParsecT Void String m a
+
+-- | Line comment group parser
+type LineGroupParser = Parsec Void [(Range, RawLineComment)]
+
+data BlockEnv = BlockEnv
+    { isLhs      :: Bool
+    , blockRange :: Range
+    }
+    deriving (Read, Show, Eq, Ord)
+
+makeLensesWith
+    (lensRules & lensField .~ mappingNamer (pure . (++ "L")))
+    ''BlockEnv
+
+-- | Block comment parser
+type BlockCommentParser = ParsecT Void String (Reader BlockEnv)
+
+-- | Prop line, with "prop>" stripped off
+newtype PropLine = PropLine {getPropLine :: String}
+    deriving (Show)
+
+-- | Example line, with @>>>@ stripped off
+newtype ExampleLine = ExampleLine {getExampleLine :: String}
+    deriving (Show)
+
+data TestComment
+    = AProp
+        { testCommentRange :: Range
+        , lineProp         :: PropLine
+        , propResults      :: [String]
+        }
+    | AnExample
+        { testCommentRange :: Range
+        , lineExamples     :: NonEmpty ExampleLine
+        , exampleResults   :: [String]
+        }
+    deriving (Show)
+
+-- | Classification of comments
+data CommentFlavour = Vanilla | HaddockNext | HaddockPrev | Named String
+    deriving (Read, Show, Eq, Ord)
+
+-- | Single line or block comments?
+data CommentStyle = Line | Block Range
+    deriving (Read, Show, Eq, Ord, Generic)
+
+makePrisms ''CommentStyle
+
+commentsToSections ::
+    -- | True if it is literate Haskell
+    Bool ->
+    Comments ->
+    Sections
+commentsToSections isLHS Comments {..} =
+    let (lineSectionSeeds, lineSetupSeeds) =
+            foldMap
+                ( \lcs ->
+                    let theRan =
+                            Range
+                                (view start $ fst $ NE.head lcs)
+                                (view end $ fst $ NE.last lcs)
+                     in case parseMaybe lineGroupP $ NE.toList lcs of
+                            Nothing -> mempty
+                            Just (mls, rs) ->
+                                ( maybe mempty (uncurry Map.singleton) ((theRan,) <$> mls)
+                                , -- orders setup sections in ascending order
+                                  if null rs
+                                    then mempty
+                                    else
+                                        Map.singleton theRan $
+                                            DL.singleton (Line, rs)
+                                )
+                )
+                $ groupLineComments $
+                    Map.filterWithKey
+                        -- FIXME:
+                        -- To comply with the initial behaviour of
+                        -- Extended Eval Plugin;
+                        -- but it also rejects modules with
+                        -- non-zero base indentation level!
+                        ( \pos _ ->
+                            if isLHS
+                                then pos ^. start . character == 2
+                                else pos ^. start . character == 0
+                        )
+                        lineComments
+        (blockSeed, blockSetupSeeds) =
+            foldMap
+                ( \(ran, lcs) ->
+                    case parseBlockMaybe isLHS ran blockCommentBP $
+                        getRawBlockComment lcs of
+                        Nothing -> mempty
+                        Just (Named "setup", grp) ->
+                            -- orders setup sections in ascending order
+                            ( mempty
+                            , Map.singleton ran $
+                                DL.singleton (Block ran, grp)
+                            )
+                        Just grp ->
+                            ( Map.singleton ran grp
+                            , mempty
+                            )
+                )
+                -- It seems Extended Eval Plugin doesn't constraint
+                -- starting indentation level for block comments.
+                -- Rather, it constrains the indentation level /inside/
+                -- block comment body.
+                $ Map.toList blockComments
+        lineSections =
+            lineSectionSeeds <&> uncurry (testsToSection Line)
+        multilineSections =
+            Map.mapWithKey
+                (uncurry . testsToSection . Block)
+                blockSeed
+        setupSections =
+            -- Setups doesn't need Dummy position
+            map
+                ( \(style, tests) ->
+                    testsToSection
+                        style
+                        (Named "setup")
+                        tests
+                )
+                $ DL.toList $
+                    F.fold $
+                        Map.unionWith (<>) lineSetupSeeds blockSetupSeeds
+        nonSetupSections = F.toList $ lineSections `Map.union` multilineSections
+     in Sections {..}
+
+parseBlockMaybe :: Bool -> Range -> BlockCommentParser a -> String -> Maybe a
+parseBlockMaybe isLhs blockRange p i =
+    case runReader (runParserT p' "" i) BlockEnv {..} of
+        Left {} -> Nothing
+        Right a -> Just a
+    where
+        p' = do
+            updateParserState $ \st ->
+                st
+                    { statePosState =
+                        (statePosState st)
+                            { pstateSourcePos = positionToSourcePos $ blockRange ^. start
+                            }
+                    }
+            p
+
+type CommentRange = Range
+
+type SectionRange = Range
+
+testsToSection ::
+    CommentStyle ->
+    CommentFlavour ->
+    [TestComment] ->
+    Section
+testsToSection style flav tests =
+    let sectionName
+            | Named name <- flav = name
+            | otherwise = ""
+        sectionLanguage = case flav of
+            HaddockNext -> Haddock
+            HaddockPrev -> Haddock
+            _           -> Plain
+        sectionTests = map fromTestComment tests
+        sectionFormat =
+            case style of
+                Line      -> SingleLine
+                Block ran -> MultiLine ran
+     in Section {..}
+
+fromTestComment :: TestComment -> Test
+fromTestComment AProp {..} =
+    Property
+        { testline = getPropLine lineProp
+        , testOutput = propResults
+        , testRange = testCommentRange
+        }
+fromTestComment AnExample {..} =
+    Example
+        { testLines = getExampleLine <$> lineExamples
+        , testOutput = exampleResults
+        , testRange = testCommentRange
+        }
+
+-- * Block comment parser
+
+{- $setup
+>>> dummyPos = Position 0 0
+>>> parseE p = either (error . errorBundlePretty) id . parse p ""
+-}
+
+-- >>> parseE (blockCommentBP True dummyPos) "{- |\n  >>> 5+5\n  11\n  -}"
+-- (HaddockNext,[AnExample {testCommentRange = Position {_line = 1, _character = 0}, lineExamples = ExampleLine {getExampleLine = " 5+5"} :| [], exampleResults = ["  11"]}])
+
+blockCommentBP ::
+    -- | True if Literate Haskell
+    BlockCommentParser (CommentFlavour, [TestComment])
+blockCommentBP = do
+    skipCount 2 anySingle -- "{-"
+    void $ optional $ char ' '
+    flav <- commentFlavourP
+    hit <- skipNormalCommentBlock
+    if hit
+        then do
+            body <-
+                many $
+                    (blockExamples <|> blockProp)
+                        <* skipNormalCommentBlock
+            void takeRest -- just consume the rest
+            pure (flav, body)
+        else pure (flav, [])
+
+skipNormalCommentBlock :: BlockCommentParser Bool
+skipNormalCommentBlock = do
+    BlockEnv {..} <- ask
+    skipManyTill (normalLineP isLhs $ Block blockRange) $
+        False <$ try (optional (chunk "-}") *> eof)
+            <|> True <$ lookAhead (try $ testSymbol isLhs $ Block blockRange)
+
+testSymbol :: Bool -> CommentStyle -> LineParser ()
+testSymbol isLHS style =
+    -- FIXME: To comply with existing Extended Eval Plugin Behaviour;
+    -- it must skip one space after a comment!
+    -- This prevents Eval Plugin from working on
+    -- modules with non-standard base indentation-level.
+    when (isLHS && is _Block style) (void $ count' 0 2 $ char ' ')
+        *> (exampleSymbol <|> propSymbol)
+
+eob :: LineParser ()
+eob = eof <|> try (optional (chunk "-}") *> eof) <|> void eol
+
+blockExamples
+    , blockProp ::
+        BlockCommentParser TestComment
+blockExamples = do
+    BlockEnv {..} <- ask
+    (ran, examples) <- withRange $ NE.some $ exampleLineStrP isLhs $ Block blockRange
+    AnExample ran examples <$> resultBlockP
+blockProp = do
+    BlockEnv {..} <- ask
+    (ran, Identity prop) <- withRange $ fmap Identity $ propLineStrP isLhs $ Block blockRange
+    AProp ran prop <$> resultBlockP
+
+withRange ::
+    (TraversableStream s, Stream s, Monad m, Ord v, Traversable t) =>
+    ParsecT v s m (t (a, Position)) ->
+    ParsecT v s m (Range, t a)
+withRange p = do
+    beg <- sourcePosToPosition <$> getSourcePos
+    as <- p
+    let fin
+            | null as = beg
+            | otherwise = snd $ last $ F.toList as
+    pure (Range beg fin, fst <$> as)
+
+resultBlockP :: BlockCommentParser [String]
+resultBlockP = do
+    BlockEnv {..} <- ask
+    many $
+        fmap fst . nonEmptyNormalLineP isLhs $
+            Block blockRange
+
+positionToSourcePos :: Position -> SourcePos
+positionToSourcePos pos =
+    P.SourcePos
+        { sourceName = "<block comment>"
+        , sourceLine = P.mkPos $ 1 + pos ^. line
+        , sourceColumn = P.mkPos $ 1 + pos ^. character
+        }
+
+sourcePosToPosition :: SourcePos -> Position
+sourcePosToPosition SourcePos {..} =
+    Position (unPos sourceLine - 1) (unPos sourceColumn - 1)
+
+-- * Line Group Parser
+
+{- |
+Result: a tuple of ordinary line tests and setting sections.
+
+TODO: Haddock comment can adjacent to vanilla comment:
+
+    @
+        -- Vanilla comment
+        -- Another vanilla
+        -- | This parses as Haddock comment as GHC
+    @
+
+This behaviour is not yet handled correctly in Eval Plugin;
+but for future extension for this, we use a tuple here instead of 'Either'.
+-}
+lineGroupP ::
+    LineGroupParser
+        (Maybe (CommentFlavour, [TestComment]), [TestComment])
+lineGroupP = do
+    (_, flav) <- lookAhead $ parseLine (commentFlavourP <* takeRest)
+    case flav of
+        Named "setup" -> (Nothing,) <$> lineCommentSectionsP
+        flav          -> (,mempty) . Just . (flav,) <$> lineCommentSectionsP
+
+-- >>>  parse (lineGroupP <*eof) "" $ (dummyPosition, ) . RawLineComment <$> ["-- a", "-- b"]
+-- Variable not in scope: dummyPosition :: Position
+
+commentFlavourP :: LineParser CommentFlavour
+commentFlavourP =
+    P.option
+        Vanilla
+        ( HaddockNext <$ char '|'
+            <|> HaddockPrev <$ char '^'
+            <|> Named <$ char '$'
+                <* optional hspace
+                <*> ((:) <$> letterChar <*> P.many alphaNumChar)
+        )
+        <* optional (char ' ')
+
+lineCommentHeadP :: LineParser ()
+lineCommentHeadP = do
+    -- and no operator symbol character follows.
+    void $ chunk "--"
+    skipMany $ char '-'
+    void $ optional $ char ' '
+
+lineCommentSectionsP ::
+    LineGroupParser [TestComment]
+lineCommentSectionsP = do
+    skipMany normalLineCommentP
+    many $
+        exampleLinesGP
+            <|> uncurry AProp <$> propLineGP <*> resultLinesP
+                <* skipMany normalLineCommentP
+
+lexemeLine :: LineGroupParser a -> LineGroupParser a
+lexemeLine p = p <* skipMany normalLineCommentP
+
+resultLinesP :: LineGroupParser [String]
+resultLinesP = many nonEmptyLGP
+
+normalLineCommentP :: LineGroupParser (Range, String)
+normalLineCommentP =
+    parseLine (fst <$ commentFlavourP <*> normalLineP False Line)
+
+nonEmptyLGP :: LineGroupParser String
+nonEmptyLGP =
+    try $
+        fmap snd $
+            parseLine $
+                fst <$ commentFlavourP <*> nonEmptyNormalLineP False Line
+
+exampleLinesGP :: LineGroupParser TestComment
+exampleLinesGP =
+    lexemeLine $
+        uncurry AnExample . first convexHullRange . NE.unzip
+            <$> NE.some exampleLineGP
+            <*> resultLinesP
+
+convexHullRange :: NonEmpty Range -> Range
+convexHullRange nes =
+    Range (NE.head nes ^. start) (NE.last nes ^. end)
+
+exampleLineGP :: LineGroupParser (Range, ExampleLine)
+exampleLineGP =
+    -- In line-comments, indentation-level inside comment doesn't matter.
+    parseLine (fst <$ commentFlavourP <*> exampleLineStrP False Line)
+
+propLineGP :: LineGroupParser (Range, PropLine)
+propLineGP =
+    -- In line-comments, indentation-level inside comment doesn't matter.
+    parseLine (fst <$ commentFlavourP <*> propLineStrP False Line)
+
+{- |
+Turning a line parser into line group parser consuming a single line comment.
+Parses a sinlge line comment, skipping prefix "--[-*]" with optional one horizontal space.
+fails if the input does not start with "--".
+
+__N.B.__ We don't strip comment flavours.
+
+>>> pck = (:[]).(:[]) . RawLineComment
+
+>>> parseMaybe (parseLine $ takeRest) $ pck "-- >>> A"
+Just [">>> A"]
+
+>>> parseMaybe (parseLine $ takeRest) $ pck "---  >>> A"
+Just [" >>> A"]
+
+>>> parseMaybe (parseLine takeRest) $ pck ""
+Nothing
+-}
+parseLine ::
+    (Ord (f RawLineComment), Traversable f) =>
+    LineParser a ->
+    Parsec Void [f RawLineComment] (f a)
+parseLine p =
+    P.token
+        (mapM $ parseMaybe (lineCommentHeadP *> p) . getRawLineComment)
+        mempty
+
+-- * Line Parsers
+
+-- | Non-empty normal line.
+nonEmptyNormalLineP ::
+    -- | True if Literate Haskell
+    Bool ->
+    CommentStyle ->
+    LineParser (String, Position)
+nonEmptyNormalLineP isLHS style = try $ do
+    (ln, pos) <- normalLineP isLHS style
+    guard $
+        case style of
+            Block{} -> T.strip (T.pack ln) `notElem` ["{-", "-}", ""]
+            _       -> not $ all C.isSpace ln
+    pure (ln, pos)
+
+{- | Normal line is a line neither a example nor prop.
+ Empty line is normal.
+-}
+normalLineP ::
+    -- | True if Literate Haskell
+    Bool ->
+    CommentStyle ->
+    LineParser (String, Position)
+normalLineP isLHS style = do
+    notFollowedBy
+        (try $ testSymbol isLHS style)
+    when (isLHS && is _Block style) $
+        void $ count' 0 2 $ char ' '
+    consume style
+
+consume :: CommentStyle -> LineParser (String, Position)
+consume style =
+    case style of
+        Line     -> (,) <$> takeRest <*> getPosition
+        Block {} -> manyTill_ anySingle (getPosition <* eob)
+
+getPosition :: (Ord v, TraversableStream s) => ParsecT v s m Position
+getPosition = sourcePosToPosition <$> getSourcePos
+
+-- | Parses example test line.
+exampleLineStrP ::
+    -- | True if Literate Haskell
+    Bool ->
+    CommentStyle ->
+    LineParser (ExampleLine, Position)
+exampleLineStrP isLHS style =
+    try $
+        -- FIXME: To comply with existing Extended Eval Plugin Behaviour;
+        -- it must skip one space after a comment!
+        -- This prevents Eval Plugin from working on
+        -- modules with non-standard base indentation-level.
+        when (isLHS && is _Block style) (void $ count' 0 2 $ char ' ')
+            *> exampleSymbol
+            *> (first ExampleLine <$> consume style)
+
+exampleSymbol :: LineParser ()
+exampleSymbol =
+    chunk ">>>" *> P.notFollowedBy (char '>')
+
+propSymbol :: LineParser ()
+propSymbol = chunk "prop>" *> P.notFollowedBy (char '>')
+
+-- | Parses prop test line.
+propLineStrP ::
+    -- | True if Literate HAskell
+    Bool ->
+    CommentStyle ->
+    LineParser (PropLine, Position)
+propLineStrP isLHS style =
+    -- FIXME: To comply with existing Extended Eval Plugin Behaviour;
+    -- it must skip one space after a comment!
+    -- This prevents Eval Plugin from working on
+    -- modules with non-standard base indentation-level.
+    when (isLHS && is _Block style) (void $ count' 0 2 $ char ' ')
+        *> chunk "prop>"
+        *> P.notFollowedBy (char '>')
+        *> (first PropLine <$> consume style)
+
+-- * Utilities
+
+{- |
+Given a sequence of tokens increasing in their starting position,
+groups them into sublists consisting of contiguous tokens;
+Two adjacent tokens are considered to be contiguous if
+
+    * line number increases by 1, and
+    * they have same starting column.
+
+>>> contiguousGroupOn id [(1,2),(2,2),(3,4),(4,4),(5,4),(7,0),(8,0)]
+[(1,2) :| [(2,2)],(3,4) :| [(4,4),(5,4)],(7,0) :| [(8,0)]]
+-}
+contiguousGroupOn :: (a -> (Int, Int)) -> [a] -> [NonEmpty a]
+contiguousGroupOn toLineCol = foldr step []
+    where
+        step a [] = [pure a]
+        step a bss0@((b :| bs) : bss)
+            | let (aLine, aCol) = toLineCol a
+              , let (bLine, bCol) = toLineCol b
+              , aLine + 1 == bLine && aCol == bCol =
+                (a :| b : bs) : bss
+            | otherwise = pure a : bss0
+
+{- | Given a map from positions, divides them into subgroup
+ with contiguous line and columns.
+-}
+groupLineComments ::
+    Map Range a -> [NonEmpty (Range, a)]
+groupLineComments =
+    contiguousGroupOn (fst >>> view start >>> view line &&& view character)
+        . Map.toList
src/Ide/Plugin/Eval/Parse/Option.hs view
@@ -1,40 +1,40 @@-{-# OPTIONS_GHC -Wwarn #-}---- | GHC language options parser-module Ide.Plugin.Eval.Parse.Option (-    langOptions,-    parseSetFlags,-) where--import Text.Megaparsec.Char-import Text.Megaparsec-import Data.Void (Void)-import Control.Arrow (left)--{- |->>> langOptions ":set   -XBinaryLiterals  -XOverloadedStrings "-Right ["BinaryLiterals","OverloadedStrings"]-->>> langOptions ":set"-Right []-->>> langOptions ""-Left "No match"--}-langOptions :: String -> Either String [String]-langOptions =-  left errorBundlePretty-  . parse (space *> languageOpts <* eof) ""--parseSetFlags :: String -> Maybe String-parseSetFlags = parseMaybe-    (hspace *> chunk ":set"-        *> hspace1 *> takeRest-        :: Parsec Void String String-    )---- >>> parseMaybe languageOpts ":set -XBinaryLiterals -XOverloadedStrings"--- Just ["BinaryLiterals","OverloadedStrings"]-languageOpts :: Parsec Void String [String]-languageOpts = string ":set" *> space1-  *> many (string "-X" *> many letterChar <* space)+{-# OPTIONS_GHC -Wwarn #-}
+
+-- | GHC language options parser
+module Ide.Plugin.Eval.Parse.Option (
+    langOptions,
+    parseSetFlags,
+) where
+
+import           Control.Arrow        (left)
+import           Data.Void            (Void)
+import           Text.Megaparsec
+import           Text.Megaparsec.Char
+
+{- |
+>>> langOptions ":set   -XBinaryLiterals  -XOverloadedStrings "
+Right ["BinaryLiterals","OverloadedStrings"]
+
+>>> langOptions ":set"
+Right []
+
+>>> langOptions ""
+Left "No match"
+-}
+langOptions :: String -> Either String [String]
+langOptions =
+  left errorBundlePretty
+  . parse (space *> languageOpts <* eof) ""
+
+parseSetFlags :: String -> Maybe String
+parseSetFlags = parseMaybe
+    (hspace *> chunk ":set"
+        *> hspace1 *> takeRest
+        :: Parsec Void String String
+    )
+
+-- >>> parseMaybe languageOpts ":set -XBinaryLiterals -XOverloadedStrings"
+-- Just ["BinaryLiterals","OverloadedStrings"]
+languageOpts :: Parsec Void String [String]
+languageOpts = string ":set" *> space1
+  *> many (string "-X" *> many letterChar <* space)
src/Ide/Plugin/Eval/Types.hs view
@@ -1,162 +1,162 @@-{-# LANGUAGE DeriveAnyClass #-}-{-# LANGUAGE DeriveFunctor #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE DerivingStrategies #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE UndecidableInstances #-}-{-# OPTIONS_GHC -Wwarn #-}--module Ide.Plugin.Eval.Types-    ( locate,-      locate0,-      Test (..),-      isProperty,-      Format (..),-      Language (..),-      Section (..),-      Sections (..),-      hasTests,-      hasPropertyTest,-      splitSections,-      Loc,-      Located (..),-      Comments (..),-      RawBlockComment (..),-      RawLineComment (..),-      unLoc,-      Txt,-      EvalParams(..),-    )-where--import Control.DeepSeq (NFData (rnf), deepseq)-import Data.Aeson (FromJSON, ToJSON)-import Data.List (partition)-import Data.List.NonEmpty (NonEmpty)-import Data.Map.Strict (Map)-import Data.String (IsString (..))-import Development.IDE (Range)-import GHC.Generics (Generic)-import qualified Text.Megaparsec as P-import Language.LSP.Types (TextDocumentIdentifier)---- | A thing with a location attached.-data Located l a = Located {location :: l, located :: a}-    deriving (Eq, Show, Ord, Functor, Generic, FromJSON, ToJSON)---- | Discard location information.-unLoc :: Located l a -> a-unLoc (Located _ a) = a--instance (NFData l, NFData a) => NFData (Located l a) where-    rnf (Located loc a) = loc `deepseq` a `deepseq` ()--type Loc = Located Line--type Line = Int--locate :: Loc [a] -> [Loc a]-locate (Located l tst) = zipWith Located [l ..] tst--locate0 :: [a] -> [Loc a]-locate0 = locate . Located 0--type Txt = String--data Sections = Sections-    { nonSetupSections :: [Section]-    , setupSections :: [Section]-    }-    deriving (Show, Eq, Generic)--data Section = Section-    { sectionName :: Txt-    , sectionTests :: [Test]-    , sectionLanguage :: Language-    , sectionFormat :: Format-    }-    deriving (Eq, Show, Generic, FromJSON, ToJSON, NFData)--hasTests :: Section -> Bool-hasTests = not . null . sectionTests--hasPropertyTest :: Section -> Bool-hasPropertyTest = any isProperty . sectionTests---- |Split setup and normal sections-splitSections :: [Section] -> ([Section], [Section])-splitSections = partition ((== "setup") . sectionName)--data Test-    = Example {testLines :: NonEmpty Txt, testOutput :: [Txt], testRange :: Range}-    | Property {testline :: Txt, testOutput :: [Txt], testRange :: Range}-    deriving (Eq, Show, Generic, FromJSON, ToJSON, NFData)--data Comments = Comments-    { lineComments :: Map Range RawLineComment-    , blockComments :: Map Range RawBlockComment-    }-    deriving (Show, Eq, Ord, Generic)--newtype RawBlockComment = RawBlockComment {getRawBlockComment :: String}-    deriving (Show, Eq, Ord)-    deriving newtype-        ( IsString-        , P.Stream-        , P.TraversableStream-        , P.VisualStream-        , Semigroup-        , Monoid-        )--newtype RawLineComment = RawLineComment {getRawLineComment :: String}-    deriving (Show, Eq, Ord)-    deriving newtype-        ( IsString-        , P.Stream-        , P.TraversableStream-        , P.VisualStream-        , Semigroup-        , Monoid-        )--instance Semigroup Comments where-    Comments ls bs <> Comments ls' bs' = Comments (ls <> ls') (bs <> bs')--instance Monoid Comments where-    mempty = Comments mempty mempty--isProperty :: Test -> Bool-isProperty Property {} = True-isProperty _ = False--data Format-    = SingleLine-    | -- | @Range@ is that of surrounding entire block comment, not section.-      -- Used for detecting no-newline test commands.-      MultiLine Range-    deriving (Eq, Show, Ord, Generic, FromJSON, ToJSON, NFData)--data Language = Plain | Haddock deriving (Eq, Show, Generic, Ord, FromJSON, ToJSON, NFData)--data ExpectedLine = ExpectedLine [LineChunk] | WildCardLine-    deriving (Eq, Show, Generic, FromJSON, ToJSON, NFData)--instance IsString ExpectedLine where-    fromString = ExpectedLine . return . LineChunk--data LineChunk = LineChunk String | WildCardChunk-    deriving (Eq, Show, Generic, FromJSON, ToJSON, NFData)--instance IsString LineChunk where-    fromString = LineChunk--type EvalId = Int---- | Specify the test section to execute-data EvalParams = EvalParams-    { sections :: [Section]-    , module_ :: !TextDocumentIdentifier-    , evalId :: !EvalId -- ^ unique group id; for test uses-    }-    deriving (Eq, Show, Generic, FromJSON, ToJSON)+{-# LANGUAGE DeriveAnyClass             #-}
+{-# LANGUAGE DeriveFunctor              #-}
+{-# LANGUAGE DeriveGeneric              #-}
+{-# LANGUAGE DerivingStrategies         #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE UndecidableInstances       #-}
+{-# OPTIONS_GHC -Wwarn #-}
+
+module Ide.Plugin.Eval.Types
+    ( locate,
+      locate0,
+      Test (..),
+      isProperty,
+      Format (..),
+      Language (..),
+      Section (..),
+      Sections (..),
+      hasTests,
+      hasPropertyTest,
+      splitSections,
+      Loc,
+      Located (..),
+      Comments (..),
+      RawBlockComment (..),
+      RawLineComment (..),
+      unLoc,
+      Txt,
+      EvalParams(..),
+    )
+where
+
+import           Control.DeepSeq    (NFData (rnf), deepseq)
+import           Data.Aeson         (FromJSON, ToJSON)
+import           Data.List          (partition)
+import           Data.List.NonEmpty (NonEmpty)
+import           Data.Map.Strict    (Map)
+import           Data.String        (IsString (..))
+import           Development.IDE    (Range)
+import           GHC.Generics       (Generic)
+import           Language.LSP.Types (TextDocumentIdentifier)
+import qualified Text.Megaparsec    as P
+
+-- | A thing with a location attached.
+data Located l a = Located {location :: l, located :: a}
+    deriving (Eq, Show, Ord, Functor, Generic, FromJSON, ToJSON)
+
+-- | Discard location information.
+unLoc :: Located l a -> a
+unLoc (Located _ a) = a
+
+instance (NFData l, NFData a) => NFData (Located l a) where
+    rnf (Located loc a) = loc `deepseq` a `deepseq` ()
+
+type Loc = Located Line
+
+type Line = Int
+
+locate :: Loc [a] -> [Loc a]
+locate (Located l tst) = zipWith Located [l ..] tst
+
+locate0 :: [a] -> [Loc a]
+locate0 = locate . Located 0
+
+type Txt = String
+
+data Sections = Sections
+    { nonSetupSections :: [Section]
+    , setupSections    :: [Section]
+    }
+    deriving (Show, Eq, Generic)
+
+data Section = Section
+    { sectionName     :: Txt
+    , sectionTests    :: [Test]
+    , sectionLanguage :: Language
+    , sectionFormat   :: Format
+    }
+    deriving (Eq, Show, Generic, FromJSON, ToJSON, NFData)
+
+hasTests :: Section -> Bool
+hasTests = not . null . sectionTests
+
+hasPropertyTest :: Section -> Bool
+hasPropertyTest = any isProperty . sectionTests
+
+-- |Split setup and normal sections
+splitSections :: [Section] -> ([Section], [Section])
+splitSections = partition ((== "setup") . sectionName)
+
+data Test
+    = Example {testLines :: NonEmpty Txt, testOutput :: [Txt], testRange :: Range}
+    | Property {testline :: Txt, testOutput :: [Txt], testRange :: Range}
+    deriving (Eq, Show, Generic, FromJSON, ToJSON, NFData)
+
+data Comments = Comments
+    { lineComments  :: Map Range RawLineComment
+    , blockComments :: Map Range RawBlockComment
+    }
+    deriving (Show, Eq, Ord, Generic)
+
+newtype RawBlockComment = RawBlockComment {getRawBlockComment :: String}
+    deriving (Show, Eq, Ord)
+    deriving newtype
+        ( IsString
+        , P.Stream
+        , P.TraversableStream
+        , P.VisualStream
+        , Semigroup
+        , Monoid
+        )
+
+newtype RawLineComment = RawLineComment {getRawLineComment :: String}
+    deriving (Show, Eq, Ord)
+    deriving newtype
+        ( IsString
+        , P.Stream
+        , P.TraversableStream
+        , P.VisualStream
+        , Semigroup
+        , Monoid
+        )
+
+instance Semigroup Comments where
+    Comments ls bs <> Comments ls' bs' = Comments (ls <> ls') (bs <> bs')
+
+instance Monoid Comments where
+    mempty = Comments mempty mempty
+
+isProperty :: Test -> Bool
+isProperty Property {} = True
+isProperty _           = False
+
+data Format
+    = SingleLine
+    | -- | @Range@ is that of surrounding entire block comment, not section.
+      -- Used for detecting no-newline test commands.
+      MultiLine Range
+    deriving (Eq, Show, Ord, Generic, FromJSON, ToJSON, NFData)
+
+data Language = Plain | Haddock deriving (Eq, Show, Generic, Ord, FromJSON, ToJSON, NFData)
+
+data ExpectedLine = ExpectedLine [LineChunk] | WildCardLine
+    deriving (Eq, Show, Generic, FromJSON, ToJSON, NFData)
+
+instance IsString ExpectedLine where
+    fromString = ExpectedLine . return . LineChunk
+
+data LineChunk = LineChunk String | WildCardChunk
+    deriving (Eq, Show, Generic, FromJSON, ToJSON, NFData)
+
+instance IsString LineChunk where
+    fromString = LineChunk
+
+type EvalId = Int
+
+-- | Specify the test section to execute
+data EvalParams = EvalParams
+    { sections :: [Section]
+    , module_  :: !TextDocumentIdentifier
+    , evalId   :: !EvalId -- ^ unique group id; for test uses
+    }
+    deriving (Eq, Show, Generic, FromJSON, ToJSON)
src/Ide/Plugin/Eval/Util.hs view
@@ -1,116 +1,105 @@-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE NoMonomorphismRestriction #-}-{-# OPTIONS_GHC -Wno-orphans #-}---- |Debug utilities-module Ide.Plugin.Eval.Util (-    asS,-    timed,-    isLiterate,-    handleMaybe,-    handleMaybeM,-    response,-    response',-    gStrictTry,-    logWith,-) where--import Control.Monad.Extra (maybeM)-import Control.Monad.IO.Class (MonadIO (liftIO))-import Control.Monad.Trans.Class (lift)-import Control.Monad.Trans.Except (-    ExceptT (..),-    runExceptT,-    throwE,- )-import Data.Aeson (Value (Null))-import Data.Bifunctor (first)-import Data.String (IsString (fromString))-import qualified Data.Text as T-import Development.IDE (-    IdeState,-    Priority (..),-    ideLogger,-    logPriority,- )-import Exception (ExceptionMonad, SomeException (..), evaluate, gcatch)-import GHC.Exts (toList)-import GHC.Stack (HasCallStack, callStack, srcLocFile, srcLocStartCol, srcLocStartLine)-import Language.LSP.Server-import Language.LSP.Types-import Outputable (-    Outputable (ppr),-    ppr,-    showSDocUnsafe,- )-import System.FilePath (takeExtension)-import System.Time.Extra (-    duration,-    showDuration,- )-import UnliftIO.Exception (catchAny)--asS :: Outputable a => a -> String-asS = showSDocUnsafe . ppr--timed :: MonadIO m => (t -> String -> m a) -> t -> m b -> m b-timed out name op = do-    (secs, r) <- duration op-    _ <- out name (showDuration secs)-    return r---- |Log using hie logger, reports source position of logging statement-logWith :: (HasCallStack, MonadIO m, Show a1, Show a2) => IdeState -> a1 -> a2 -> m ()-logWith state key val =-    liftIO . logPriority (ideLogger state) logLevel $-        T.unwords-            [T.pack logWithPos, asT key, asT val]-  where-    logWithPos =-        let stk = toList callStack-            pr pos = concat [srcLocFile pos, ":", show . srcLocStartLine $ pos, ":", show . srcLocStartCol $ pos]-         in if null stk then "" else pr . snd . head $ stk--    asT :: Show a => a -> T.Text-    asT = T.pack . show---- | Set to Info to see extensive debug info in hie log, set to Debug in production-logLevel :: Priority-logLevel = Debug -- Info--isLiterate :: FilePath -> Bool-isLiterate x = takeExtension x `elem` [".lhs", ".lhs-boot"]--handleMaybe :: Monad m => e -> Maybe b -> ExceptT e m b-handleMaybe msg = maybe (throwE msg) return--handleMaybeM :: Monad m => e -> m (Maybe b) -> ExceptT e m b-handleMaybeM msg act = maybeM (throwE msg) return $ lift act--response :: Functor f => ExceptT String f c -> f (Either ResponseError c)-response =-    fmap (first (\msg -> ResponseError InternalError (fromString msg) Nothing))-        . runExceptT--response' :: ExceptT String (LspM c) WorkspaceEdit -> LspM c (Either ResponseError Value)-response' act = do-    res <- runExceptT act-             `catchAny` showErr-    case res of-      Left e ->-          return $ Left (ResponseError InternalError (fromString e) Nothing)-      Right a -> do-        _ <- sendRequest SWorkspaceApplyEdit (ApplyWorkspaceEditParams Nothing a) (\_ -> pure ())-        return $ Right Null--gStrictTry :: ExceptionMonad m => m b -> m (Either String b)-gStrictTry op =-    gcatch-        (op >>= fmap Right . gevaluate)-        showErr--gevaluate :: MonadIO m => a -> m a-gevaluate = liftIO . evaluate--showErr :: Monad m => SomeException -> m (Either String b)-showErr = return . Left . show+{-# LANGUAGE NoMonomorphismRestriction #-}
+{-# LANGUAGE ScopedTypeVariables       #-}
+{-# OPTIONS_GHC -Wno-orphans #-}
+
+-- |Debug utilities
+module Ide.Plugin.Eval.Util (
+    asS,
+    timed,
+    isLiterate,
+    handleMaybe,
+    handleMaybeM,
+    response,
+    response',
+    gStrictTry,
+    logWith,
+) where
+
+import           Control.Monad.Extra        (maybeM)
+import           Control.Monad.IO.Class     (MonadIO (liftIO))
+import           Control.Monad.Trans.Class  (lift)
+import           Control.Monad.Trans.Except (ExceptT (..), runExceptT, throwE)
+import           Data.Aeson                 (Value (Null))
+import           Data.Bifunctor             (first)
+import           Data.String                (IsString (fromString))
+import qualified Data.Text                  as T
+import           Development.IDE            (IdeState, Priority (..), ideLogger,
+                                             logPriority)
+import           Exception                  (ExceptionMonad, SomeException (..),
+                                             evaluate, gcatch)
+import           GHC.Exts                   (toList)
+import           GHC.Stack                  (HasCallStack, callStack,
+                                             srcLocFile, srcLocStartCol,
+                                             srcLocStartLine)
+import           Language.LSP.Server
+import           Language.LSP.Types
+import           Outputable                 (Outputable (ppr), ppr,
+                                             showSDocUnsafe)
+import           System.FilePath            (takeExtension)
+import           System.Time.Extra          (duration, showDuration)
+import           UnliftIO.Exception         (catchAny)
+
+asS :: Outputable a => a -> String
+asS = showSDocUnsafe . ppr
+
+timed :: MonadIO m => (t -> String -> m a) -> t -> m b -> m b
+timed out name op = do
+    (secs, r) <- duration op
+    _ <- out name (showDuration secs)
+    return r
+
+-- |Log using hie logger, reports source position of logging statement
+logWith :: (HasCallStack, MonadIO m, Show a1, Show a2) => IdeState -> a1 -> a2 -> m ()
+logWith state key val =
+    liftIO . logPriority (ideLogger state) logLevel $
+        T.unwords
+            [T.pack logWithPos, asT key, asT val]
+  where
+    logWithPos =
+        let stk = toList callStack
+            pr pos = concat [srcLocFile pos, ":", show . srcLocStartLine $ pos, ":", show . srcLocStartCol $ pos]
+         in if null stk then "" else pr . snd . head $ stk
+
+    asT :: Show a => a -> T.Text
+    asT = T.pack . show
+
+-- | Set to Info to see extensive debug info in hie log, set to Debug in production
+logLevel :: Priority
+logLevel = Debug -- Info
+
+isLiterate :: FilePath -> Bool
+isLiterate x = takeExtension x `elem` [".lhs", ".lhs-boot"]
+
+handleMaybe :: Monad m => e -> Maybe b -> ExceptT e m b
+handleMaybe msg = maybe (throwE msg) return
+
+handleMaybeM :: Monad m => e -> m (Maybe b) -> ExceptT e m b
+handleMaybeM msg act = maybeM (throwE msg) return $ lift act
+
+response :: Functor f => ExceptT String f c -> f (Either ResponseError c)
+response =
+    fmap (first (\msg -> ResponseError InternalError (fromString msg) Nothing))
+        . runExceptT
+
+response' :: ExceptT String (LspM c) WorkspaceEdit -> LspM c (Either ResponseError Value)
+response' act = do
+    res <- runExceptT act
+             `catchAny` showErr
+    case res of
+      Left e ->
+          return $ Left (ResponseError InternalError (fromString e) Nothing)
+      Right a -> do
+        _ <- sendRequest SWorkspaceApplyEdit (ApplyWorkspaceEditParams Nothing a) (\_ -> pure ())
+        return $ Right Null
+
+gStrictTry :: ExceptionMonad m => m b -> m (Either String b)
+gStrictTry op =
+    gcatch
+        (op >>= fmap Right . gevaluate)
+        showErr
+
+gevaluate :: MonadIO m => a -> m a
+gevaluate = liftIO . evaluate
+
+showErr :: Monad m => SomeException -> m (Either String b)
+showErr = return . Left . show
+ test/Main.hs view
@@ -0,0 +1,218 @@+{-# LANGUAGE DuplicateRecordFields #-}
+{-# LANGUAGE OverloadedStrings     #-}
+{-# LANGUAGE RecordWildCards       #-}
+{-# LANGUAGE ScopedTypeVariables   #-}
+{-# LANGUAGE ViewPatterns          #-}
+
+module Main (
+    main,
+) where
+
+import           Control.Lens            (_Just, preview, view)
+import           Control.Monad           (when)
+import           Data.Aeson              (fromJSON)
+import           Data.Aeson.Types        (Result (Success))
+import           Data.List.Extra         (nubOrdOn)
+import qualified Data.Text               as T
+import qualified Data.Text.IO            as T
+import qualified Ide.Plugin.Eval         as Eval
+import           Ide.Plugin.Eval.Types   (EvalParams (..))
+import           Language.LSP.Types.Lens (command, range, title)
+import           System.Directory        (doesFileExist)
+import           System.FilePath         ((<.>), (</>))
+import           Test.Hls
+
+main :: IO ()
+main = defaultTestRunner tests
+
+evalPlugin :: PluginDescriptor IdeState
+evalPlugin = Eval.descriptor "eval"
+
+tests :: TestTree
+tests =
+    testGroup
+        "eval"
+        [ testCase "Produces Evaluate code lenses" $
+            runSessionWithServer evalPlugin evalPath $ do
+                doc <- openDoc "T1.hs" "haskell"
+                lenses <- getEvalCodeLenses doc
+                liftIO $ map (preview $ command . _Just . title) lenses @?= [Just "Evaluate..."]
+        , testCase "Produces Refresh code lenses" $
+            runSessionWithServer evalPlugin evalPath $ do
+                doc <- openDoc "T2.hs" "haskell"
+                lenses <- getEvalCodeLenses doc
+                liftIO $ map (preview $ command . _Just . title) lenses @?= [Just "Refresh..."]
+        , testCase "Code lenses have ranges" $
+            runSessionWithServer evalPlugin evalPath $ do
+                doc <- openDoc "T1.hs" "haskell"
+                lenses <- getEvalCodeLenses doc
+                liftIO $ map (view range) lenses @?= [Range (Position 4 0) (Position 5 0)]
+        , testCase "Multi-line expressions have a multi-line range" $ do
+            runSessionWithServer evalPlugin evalPath $ do
+                doc <- openDoc "T3.hs" "haskell"
+                lenses <- getEvalCodeLenses doc
+                liftIO $ map (view range) lenses @?= [Range (Position 3 0) (Position 5 0)]
+        , testCase "Executed expressions range covers only the expression" $ do
+            runSessionWithServer evalPlugin evalPath $ do
+                doc <- openDoc "T2.hs" "haskell"
+                lenses <- getEvalCodeLenses doc
+                liftIO $ map (view range) lenses @?= [Range (Position 4 0) (Position 5 0)]
+        , testCase "Evaluation of expressions" $ goldenTest "T1.hs"
+        , testCase "Reevaluation of expressions" $ goldenTest "T2.hs"
+        , testCase "Evaluation of expressions w/ imports" $ goldenTest "T3.hs"
+        , testCase "Evaluation of expressions w/ lets" $ goldenTest "T4.hs"
+        , testCase "Refresh an evaluation" $ goldenTest "T5.hs"
+        , testCase "Refresh an evaluation w/ lets" $ goldenTest "T6.hs"
+        , testCase "Refresh a multiline evaluation" $ goldenTest "T7.hs"
+        , testCase "Semantic and Lexical errors are reported" $ goldenTest "T8.hs"
+        , testCase "Applies file LANGUAGE extensions" $ goldenTest "T9.hs"
+        , testCase "Evaluate a type with :kind!" $ goldenTest "T10.hs"
+        , testCase "Reports an error for an incorrect type with :kind!" $
+            goldenTest "T11.hs"
+        , testCase "Shows a kind with :kind" $ goldenTest "T12.hs"
+        , testCase "Reports an error for an incorrect type with :kind" $
+            goldenTest "T13.hs"
+        , testCase "Returns a fully-instantiated type for :type" $
+            goldenTest "T14.hs"
+        , testCase "Returns an uninstantiated type for :type +v, admitting multiple whitespaces around arguments" $
+            goldenTest "T15.hs"
+        , testCase "Returns defaulted type for :type +d, admitting multiple whitespaces around arguments" $
+            goldenTest "T16.hs"
+        , testCase ":type reports an error when given with unknown +x option" $
+            goldenTest "T17.hs"
+        , testCase "Reports an error when given with unknown command" $
+            goldenTest "T18.hs"
+        , testCase "Returns defaulted type for :type +d reflecting the default declaration specified in the >>> prompt" $
+            goldenTest "T19.hs"
+        , expectFailBecause "known issue - see a note in P.R. #361" $
+            testCase ":type +d reflects the `default' declaration of the module" $
+                goldenTest "T20.hs"
+        , testCase ":type handles a multilined result properly" $
+            goldenTest "T21.hs"
+        , testCase ":t behaves exactly the same as :type" $
+            goldenTest "T22.hs"
+        , testCase ":type does \"dovetails\" for short identifiers" $
+            goldenTest "T23.hs"
+        , testCase ":kind! treats a multilined result properly" $
+            goldenTest "T24.hs"
+        , testCase ":kind treats a multilined result properly" $
+            goldenTest "T25.hs"
+        , testCase "local imports" $
+            goldenTest "T26.hs"
+        , testCase "Preserves one empty comment line after prompt" $
+            goldenTest "T27.hs"
+        , testCase
+            "Multi line comments"
+            $ goldenTest "TMulti.hs"
+        , testCase
+            "Multi line comments, with the last test line ends without newline"
+            $ goldenTest "TEndingMulti.hs"
+        , testCase
+            "Evaluate expressions in Plain comments in both single line and multi line format"
+            $ goldenTest "TPlainComment.hs"
+        , testCase
+            "Evaluate expressions in Haddock comments in both single line and multi line format"
+            $ goldenTest "THaddock.hs"
+        , testCase "Compare results (for Haddock tests only)" $
+            goldenTest "TCompare.hs"
+        , testCase "Local Modules imports are accessible in a test" $
+            goldenTest "TLocalImport.hs"
+        , -- , testCase "Local Modules can be imported in a test" $ goldenTest "TLocalImportInTest.hs"
+          expectFailBecause "Unexplained but minor issue" $
+            testCase "Setting language option TupleSections" $
+                goldenTest "TLanguageOptionsTupleSections.hs"
+        , testCase ":set accepts ghci flags" $
+            goldenTest "TFlags.hs"
+        , testCase "IO expressions are supported, stdout/stderr output is ignored" $
+            goldenTest "TIO.hs"
+        , testCase "Property checking" $ goldenTest "TProperty.hs"
+        , testCase
+            "Prelude has no special treatment, it is imported as stated in the module"
+            $ goldenTest "TPrelude.hs"
+        , testCase
+            "Don't panic on {-# UNPACK #-} pragma"
+            $ goldenTest "TUNPACK.hs"
+        , testCase
+            "Can handle eval inside nested comment properly"
+            $ goldenTest "TNested.hs"
+        , testCase "Test on last line insert results correctly" $ do
+                let mdl = "TLastLine.hs"
+                -- Write the test file, to make sure that it has no final line return
+                writeFile (evalPath </> mdl) "module TLastLine where\n\n-- >>> take 3 [1..]"
+                goldenTest mdl
+            , testGroup "with preprocessors"
+            [ knownBrokenInEnv [HostOS Windows, GhcVer GHC84, GhcVer GHC86]
+                "CPP eval on Windows and/or GHC <= 8.6 fails for some reasons" $
+              testCase "CPP support" $ goldenTest "TCPP.hs"
+            , knownBrokenForGhcVersions [GHC84, GHC86]
+                "Preprocessor known to fail on GHC <= 8.6"
+                $ testCase "Literate Haskell Bird Style" $ goldenTest "TLHS.lhs"
+            -- , testCase "Literate Haskell LaTeX Style" $ goldenTest "TLHSLateX.lhs"
+            ]
+        , testCase "Works with NoImplicitPrelude"
+            $ goldenTest "TNoImplicitPrelude.hs"
+        , testCase "Variable 'it' works"
+            $ goldenTest "TIt.hs"
+        ]
+
+goldenTest :: FilePath -> IO ()
+goldenTest = goldenTestBy isEvalTest
+
+{- |Execute all CodeLens accepted by 'filter'
+ Compare results with the contents of corresponding '.expected' file (and creates it, if missing)
+-}
+goldenTestBy :: (CodeLens -> Bool) -> FilePath -> IO ()
+goldenTestBy fltr input = runSessionWithServer evalPlugin evalPath $ do
+    doc <- openDoc input "haskell"
+
+    -- Execute lenses backwards, to avoid affecting their position in the source file
+    codeLenses <- reverse <$> getCodeLensesBy fltr doc
+    -- liftIO $ print codeLenses
+
+    -- Execute sequentially, nubbing elements to avoid
+    -- evaluating the same section with multiple tests
+    -- more than twice
+    mapM_ executeCmd
+        $ nubOrdOn actSectionId [c | CodeLens{_command = Just c} <- codeLenses]
+
+    edited <- replaceUnicodeQuotes <$> documentContents doc
+    -- liftIO $ T.putStrLn edited
+
+    let expectedFile = input <.> "expected"
+
+    liftIO $ do
+        -- Write expected file if missing
+        missingExpected <- not <$> doesFileExist expectedFile
+        when missingExpected $ T.writeFile expectedFile edited
+        expected <- T.readFile expectedFile
+        edited @?= expected
+
+actSectionId :: Command -> Int
+actSectionId Command{_arguments = Just (List [fromJSON -> Success EvalParams{..}])} = evalId
+actSectionId _ = error "Invalid CodeLens"
+
+getEvalCodeLenses :: TextDocumentIdentifier -> Session [CodeLens]
+getEvalCodeLenses = getCodeLensesBy isEvalTest
+
+getCodeLensesBy :: (CodeLens -> Bool) -> TextDocumentIdentifier -> Session [CodeLens]
+getCodeLensesBy f doc = filter f <$> getCodeLenses doc
+
+-- Execute command and wait for result
+executeCmd :: Command -> Session ()
+executeCmd cmd = do
+    executeCommand cmd
+    _resp <- skipManyTill anyMessage (message SWorkspaceApplyEdit)
+    -- liftIO $ print _resp
+    return ()
+
+-- Execute only Eval tests to avoid interference from other plugins (e.g ghcide:typesignature.add)
+isEvalTest :: CodeLens -> Bool
+isEvalTest (CodeLens _ (Just (Command _ cmd _)) _)
+    | ":eval:" `T.isInfixOf` cmd = True
+isEvalTest _ = False
+
+replaceUnicodeQuotes :: T.Text -> T.Text
+replaceUnicodeQuotes = T.replace "‘" "'" . T.replace "’" "'"
+
+evalPath :: FilePath
+evalPath = "test" </> "testdata"
+ test/testdata/T1.hs view
@@ -0,0 +1,7 @@+module T1 where
+
+import Data.List (unwords)
+
+-- >>> unwords example
+example :: [String]
+example = ["This","is","an","example","of","evaluation"]
+ test/testdata/T1.hs.expected view
@@ -0,0 +1,8 @@+module T1 where
+
+import Data.List (unwords)
+
+-- >>> unwords example
+-- "This is an example of evaluation"
+example :: [String]
+example = ["This","is","an","example","of","evaluation"]
+ test/testdata/T10.hs view
@@ -0,0 +1,9 @@+{-# LANGUAGE DataKinds, TypeOperators #-}
+module T10 where
+import GHC.TypeNats ( type (+) )
+
+type Dummy = 1 + 1
+
+-- >>> type N = 1
+-- >>> type M = 40
+-- >>> :kind! N + M + 1
+ test/testdata/T10.hs.expected view
@@ -0,0 +1,11 @@+{-# LANGUAGE DataKinds, TypeOperators #-}
+module T10 where
+import GHC.TypeNats ( type (+) )
+
+type Dummy = 1 + 1
+
+-- >>> type N = 1
+-- >>> type M = 40
+-- >>> :kind! N + M + 1
+-- N + M + 1 :: Nat
+-- = 42
+ test/testdata/T11.hs view
@@ -0,0 +1,3 @@+module T11 where
+
+-- >>> :kind! a
+ test/testdata/T11.hs.expected view
@@ -0,0 +1,4 @@+module T11 where
+
+-- >>> :kind! a
+-- Not in scope: type variable 'a'
+ test/testdata/T12.hs view
@@ -0,0 +1,9 @@+{-# LANGUAGE DataKinds, TypeOperators #-}
+module T12 where
+import GHC.TypeNats ( type (+) )
+
+type Dummy = 1 + 1
+
+-- >>> type N = 1
+-- >>> type M = 40
+-- >>> :kind N + M + 1
+ test/testdata/T12.hs.expected view
@@ -0,0 +1,10 @@+{-# LANGUAGE DataKinds, TypeOperators #-}
+module T12 where
+import GHC.TypeNats ( type (+) )
+
+type Dummy = 1 + 1
+
+-- >>> type N = 1
+-- >>> type M = 40
+-- >>> :kind N + M + 1
+-- N + M + 1 :: Nat
+ test/testdata/T13.hs view
@@ -0,0 +1,3 @@+module T13 where
+
+-- >>> :kind a
+ test/testdata/T13.hs.expected view
@@ -0,0 +1,4 @@+module T13 where
+
+-- >>> :kind a
+-- Not in scope: type variable 'a'
+ test/testdata/T14.hs view
@@ -0,0 +1,7 @@+{-# LANGUAGE TypeApplications #-}
+module T14 where
+
+foo :: Show a => a -> String
+foo = show
+
+-- >>> :type foo @Int
+ test/testdata/T14.hs.expected view
@@ -0,0 +1,8 @@+{-# LANGUAGE TypeApplications #-}
+module T14 where
+
+foo :: Show a => a -> String
+foo = show
+
+-- >>> :type foo @Int
+-- foo @Int :: Int -> String
+ test/testdata/T15.hs view
@@ -0,0 +1,7 @@+{-# LANGUAGE TypeApplications #-}
+module T15 where
+
+foo :: Show a => a -> String
+foo = show
+
+-- >>> :type  +v  foo @Int
+ test/testdata/T15.hs.expected view
@@ -0,0 +1,8 @@+{-# LANGUAGE TypeApplications #-}
+module T15 where
+
+foo :: Show a => a -> String
+foo = show
+
+-- >>> :type  +v  foo @Int
+-- foo @Int :: Show Int => Int -> String
+ test/testdata/T16.hs view
@@ -0,0 +1,3 @@+module T16 where
+
+-- >>> :type  +d   40+ 2 
+ test/testdata/T16.hs.expected view
@@ -0,0 +1,4 @@+module T16 where
+
+-- >>> :type  +d   40+ 2 
+-- 40+ 2 :: Integer
+ test/testdata/T17.hs view
@@ -0,0 +1,3 @@+module T17 where
+
+-- >>> :type  +no 42
+ test/testdata/T17.hs.expected view
@@ -0,0 +1,4 @@+module T17 where
+
+-- >>> :type  +no 42
+-- parse error on input '+'
+ test/testdata/T18.hs view
@@ -0,0 +1,4 @@+{-# LANGUAGE TypeApplications #-}
+module T18 where
+
+-- >>> :noooop foo bar
+ test/testdata/T18.hs.expected view
@@ -0,0 +1,5 @@+{-# LANGUAGE TypeApplications #-}
+module T18 where
+
+-- >>> :noooop foo bar
+-- unknown command 'noooop'
+ test/testdata/T19.hs view
@@ -0,0 +1,6 @@+module T19 where
+import Data.Word (Word)
+type W = Word
+
+-- >>> default (Word)
+-- >>> :type  +d   40+ 2 
+ test/testdata/T19.hs.expected view
@@ -0,0 +1,7 @@+module T19 where
+import Data.Word (Word)
+type W = Word
+
+-- >>> default (Word)
+-- >>> :type  +d   40+ 2 
+-- 40+ 2 :: Word
+ test/testdata/T2.hs view
@@ -0,0 +1,8 @@+module T2 where
+
+import Data.List (unwords)
+
+-- >>> unwords example
+-- "Stale output"
+example :: [String]
+example = ["This","is","an","example","of","evaluation"]
+ test/testdata/T2.hs.expected view
@@ -0,0 +1,8 @@+module T2 where
+
+import Data.List (unwords)
+
+-- >>> unwords example
+-- "This is an example of evaluation"
+example :: [String]
+example = ["This","is","an","example","of","evaluation"]
+ test/testdata/T20.hs view
@@ -0,0 +1,6 @@+module T20 where
+import Data.Word (Word)
+
+default (Word)
+
+-- >>> :type  +d   40+ 2 
+ test/testdata/T20.hs.expected view
@@ -0,0 +1,7 @@+module T20 where
+import Data.Word (Word)
+
+default (Word)
+
+-- >>> :type  +d   40+ 2 
+-- 40+ 2 :: Word
+ test/testdata/T21.hs view
@@ -0,0 +1,12 @@+{-# LANGUAGE PolyKinds           #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+module T21 where
+import Data.Proxy (Proxy(..))
+import GHC.TypeNats (KnownNat)
+import Type.Reflection (Typeable)
+
+fun :: forall k n a. (KnownNat k, KnownNat n, Typeable a)
+    => Proxy k -> Proxy n -> Proxy a -> ()
+fun _ _ _ = ()
+
+-- >>> :type fun
+ test/testdata/T21.hs.expected view
@@ -0,0 +1,16 @@+{-# LANGUAGE PolyKinds           #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+module T21 where
+import Data.Proxy (Proxy(..))
+import GHC.TypeNats (KnownNat)
+import Type.Reflection (Typeable)
+
+fun :: forall k n a. (KnownNat k, KnownNat n, Typeable a)
+    => Proxy k -> Proxy n -> Proxy a -> ()
+fun _ _ _ = ()
+
+-- >>> :type fun
+-- fun
+--   :: forall k1 (k2 :: Nat) (n :: Nat) (a :: k1).
+--      (KnownNat k2, KnownNat n, Typeable a) =>
+--      Proxy k2 -> Proxy n -> Proxy a -> ()
+ test/testdata/T22.hs view
@@ -0,0 +1,9 @@+module T22 where
+import Data.Proxy      (Proxy (..))
+import GHC.TypeNats    (KnownNat)
+import Type.Reflection (Typeable)
+
+f :: Integer
+f = 32
+
+-- >>> :t f
+ test/testdata/T22.hs.expected view
@@ -0,0 +1,10 @@+module T22 where
+import Data.Proxy      (Proxy (..))
+import GHC.TypeNats    (KnownNat)
+import Type.Reflection (Typeable)
+
+f :: Integer
+f = 32
+
+-- >>> :t f
+-- f :: Integer
+ test/testdata/T23.hs view
@@ -0,0 +1,12 @@+{-# LANGUAGE PolyKinds           #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+module T23 where
+import Data.Proxy      (Proxy (..))
+import GHC.TypeNats    (KnownNat)
+import Type.Reflection (Typeable)
+
+f :: forall k n a. (KnownNat k, KnownNat n, Typeable a)
+  => Proxy k -> Proxy n -> Proxy a -> ()
+f _ _ _ = ()
+
+-- >>> :type f
+ test/testdata/T23.hs.expected view
@@ -0,0 +1,15 @@+{-# LANGUAGE PolyKinds           #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+module T23 where
+import Data.Proxy      (Proxy (..))
+import GHC.TypeNats    (KnownNat)
+import Type.Reflection (Typeable)
+
+f :: forall k n a. (KnownNat k, KnownNat n, Typeable a)
+  => Proxy k -> Proxy n -> Proxy a -> ()
+f _ _ _ = ()
+
+-- >>> :type f
+-- f :: forall k1 (k2 :: Nat) (n :: Nat) (a :: k1).
+--      (KnownNat k2, KnownNat n, Typeable a) =>
+--      Proxy k2 -> Proxy n -> Proxy a -> ()
+ test/testdata/T24.hs view
@@ -0,0 +1,14 @@+{-# LANGUAGE DataKinds            #-}
+{-# LANGUAGE PolyKinds            #-}
+{-# LANGUAGE TypeFamilies         #-}
+{-# LANGUAGE TypeOperators        #-}
+{-# LANGUAGE UndecidableInstances #-}
+module T24 where
+import GHC.TypeNats (type (-))
+data Proxy a = Stop | Next (Proxy a)
+
+type family LongP n a where
+  LongP 0 a = a
+  LongP n a = Next (LongP (n - 1) a)
+
+-- >>> :kind! ((LongP 10 Stop) :: Proxy (Proxy (Proxy (Proxy (Proxy (Proxy (Proxy (Proxy (Proxy (Proxy (Proxy (Proxy (Proxy Double)))))))))))))
+ test/testdata/T24.hs.expected view
@@ -0,0 +1,32 @@+{-# LANGUAGE DataKinds            #-}
+{-# LANGUAGE PolyKinds            #-}
+{-# LANGUAGE TypeFamilies         #-}
+{-# LANGUAGE TypeOperators        #-}
+{-# LANGUAGE UndecidableInstances #-}
+module T24 where
+import GHC.TypeNats (type (-))
+data Proxy a = Stop | Next (Proxy a)
+
+type family LongP n a where
+  LongP 0 a = a
+  LongP n a = Next (LongP (n - 1) a)
+
+-- >>> :kind! ((LongP 10 Stop) :: Proxy (Proxy (Proxy (Proxy (Proxy (Proxy (Proxy (Proxy (Proxy (Proxy (Proxy (Proxy (Proxy Double)))))))))))))
+-- ((LongP 10 Stop) :: Proxy (Proxy (Proxy (Proxy (Proxy (Proxy (Proxy (Proxy (Proxy (Proxy (Proxy (Proxy (Proxy Double))))))))))))) :: Proxy
+--                                                                                                                                        (Proxy
+--                                                                                                                                           (Proxy
+--                                                                                                                                              (Proxy
+--                                                                                                                                                 (Proxy
+--                                                                                                                                                    (Proxy
+--                                                                                                                                                       (Proxy
+--                                                                                                                                                          (Proxy
+--                                                                                                                                                             (Proxy
+--                                                                                                                                                                (Proxy
+--                                                                                                                                                                   (Proxy
+--                                                                                                                                                                      (Proxy
+--                                                                                                                                                                         (Proxy
+--                                                                                                                                                                            Double))))))))))))
+-- = 'Next
+--     ('Next
+--        ('Next
+--           ('Next ('Next ('Next ('Next ('Next ('Next ('Next 'Stop)))))))))
+ test/testdata/T25.hs view
@@ -0,0 +1,14 @@+{-# LANGUAGE DataKinds            #-}
+{-# LANGUAGE PolyKinds            #-}
+{-# LANGUAGE TypeFamilies         #-}
+{-# LANGUAGE TypeOperators        #-}
+{-# LANGUAGE UndecidableInstances #-}
+module T25 where
+import GHC.TypeNats (type (-))
+data Proxy a = Stop | Next (Proxy a)
+
+type family LongP n a where
+  LongP 0 a = a
+  LongP n a = Next (LongP (n - 1) a)
+
+-- >>> :kind (Stop :: Proxy (Proxy (Proxy (Proxy (Proxy (Proxy (Proxy (Proxy (Proxy (Proxy (Proxy (Proxy (Proxy Double)))))))))))))
+ test/testdata/T25.hs.expected view
@@ -0,0 +1,28 @@+{-# LANGUAGE DataKinds            #-}
+{-# LANGUAGE PolyKinds            #-}
+{-# LANGUAGE TypeFamilies         #-}
+{-# LANGUAGE TypeOperators        #-}
+{-# LANGUAGE UndecidableInstances #-}
+module T25 where
+import GHC.TypeNats (type (-))
+data Proxy a = Stop | Next (Proxy a)
+
+type family LongP n a where
+  LongP 0 a = a
+  LongP n a = Next (LongP (n - 1) a)
+
+-- >>> :kind (Stop :: Proxy (Proxy (Proxy (Proxy (Proxy (Proxy (Proxy (Proxy (Proxy (Proxy (Proxy (Proxy (Proxy Double)))))))))))))
+-- (Stop :: Proxy (Proxy (Proxy (Proxy (Proxy (Proxy (Proxy (Proxy (Proxy (Proxy (Proxy (Proxy (Proxy Double))))))))))))) :: Proxy
+--                                                                                                                             (Proxy
+--                                                                                                                                (Proxy
+--                                                                                                                                   (Proxy
+--                                                                                                                                      (Proxy
+--                                                                                                                                         (Proxy
+--                                                                                                                                            (Proxy
+--                                                                                                                                               (Proxy
+--                                                                                                                                                  (Proxy
+--                                                                                                                                                     (Proxy
+--                                                                                                                                                        (Proxy
+--                                                                                                                                                           (Proxy
+--                                                                                                                                                              (Proxy
+--                                                                                                                                                                 Double))))))))))))
+ test/testdata/T26.hs view
@@ -0,0 +1,4 @@+module T26 where
+import T25 ()
+
+-- >>> "hello"
+ test/testdata/T26.hs.expected view
@@ -0,0 +1,5 @@+module T26 where
+import T25 ()
+
+-- >>> "hello"
+-- "hello"
+ test/testdata/T27.hs view
@@ -0,0 +1,4 @@+module T27 where
+
+-- >>> ()
+--
+ test/testdata/T27.hs.expected view
@@ -0,0 +1,5 @@+module T27 where
+
+-- >>> ()
+-- ()
+--
+ test/testdata/T3.hs view
@@ -0,0 +1,7 @@+module T3 where
+
+
+-- >>> import Data.List (unwords)
+-- >>> unwords example
+example :: [String]
+example = ["This","is","an","example","of","evaluation"]
+ test/testdata/T3.hs.expected view
@@ -0,0 +1,8 @@+module T3 where
+
+
+-- >>> import Data.List (unwords)
+-- >>> unwords example
+-- "This is an example of evaluation"
+example :: [String]
+example = ["This","is","an","example","of","evaluation"]
+ test/testdata/T4.hs view
@@ -0,0 +1,9 @@+module T4 where
+
+import           Data.List (unwords)
+
+-- >>> let evaluation = " evaluation"
+-- >>> unwords example ++ evaluation
+example :: [String]
+example = ["This","is","an","example","of"]
+
+ test/testdata/T4.hs.expected view
@@ -0,0 +1,10 @@+module T4 where
+
+import           Data.List (unwords)
+
+-- >>> let evaluation = " evaluation"
+-- >>> unwords example ++ evaluation
+-- "This is an example of evaluation"
+example :: [String]
+example = ["This","is","an","example","of"]
+
+ test/testdata/T5.hs view
@@ -0,0 +1,6 @@+module T5 where
+
+-- >>> unwords example
+-- "This is a stale example of evaluation"
+example :: [String]
+example = ["This","is","an","example","of","evaluation"]
+ test/testdata/T5.hs.expected view
@@ -0,0 +1,6 @@+module T5 where
+
+-- >>> unwords example
+-- "This is an example of evaluation"
+example :: [String]
+example = ["This","is","an","example","of","evaluation"]
+ test/testdata/T6.hs view
@@ -0,0 +1,9 @@+module T6 where
+
+import Data.List (unwords)
+
+-- >>> let evaluation = " evaluation"
+-- >>> unwords example ++ evaluation
+-- "This is a stale example of evaluation"
+example :: [String]
+example = ["This","is","an","example","of"]
+ test/testdata/T6.hs.expected view
@@ -0,0 +1,9 @@+module T6 where
+
+import Data.List (unwords)
+
+-- >>> let evaluation = " evaluation"
+-- >>> unwords example ++ evaluation
+-- "This is an example of evaluation"
+example :: [String]
+example = ["This","is","an","example","of"]
+ test/testdata/T7.hs view
@@ -0,0 +1,10 @@+module T7 where
+
+import Data.List (unwords)
+
+-- >>> --
+-- >>> --
+-- >>> unwords example
+-- "This is a stale example of"
+example :: [String]
+example = ["This","is","an","example","of"]
+ test/testdata/T7.hs.expected view
@@ -0,0 +1,10 @@+module T7 where
+
+import Data.List (unwords)
+
+-- >>> --
+-- >>> --
+-- >>> unwords example
+-- "This is an example of"
+example :: [String]
+example = ["This","is","an","example","of"]
+ test/testdata/T8.hs view
@@ -0,0 +1,10 @@+-- Semantic and Lexical errors are reported
+module T8 where
+
+-- >>> noFunctionWithThisName
+
+-- >>> "a" + "bc"
+
+-- >>> "
+
+-- >>> 3 `div` 0
+ test/testdata/T8.hs.expected view
@@ -0,0 +1,14 @@+-- Semantic and Lexical errors are reported
+module T8 where
+
+-- >>> noFunctionWithThisName
+-- Variable not in scope: noFunctionWithThisName
+
+-- >>> "a" + "bc"
+-- No instance for (Num [Char]) arising from a use of '+'
+
+-- >>> "
+-- lexical error in string/character literal at end of input
+
+-- >>> 3 `div` 0
+-- divide by zero
+ test/testdata/T9.hs view
@@ -0,0 +1,7 @@+{-# LANGUAGE DataKinds #-}
+module T9 where
+import Data.Proxy (Proxy(..))
+
+type P = Proxy
+
+-- >>> Proxy :: Proxy 3
+ test/testdata/T9.hs.expected view
@@ -0,0 +1,8 @@+{-# LANGUAGE DataKinds #-}
+module T9 where
+import Data.Proxy (Proxy(..))
+
+type P = Proxy
+
+-- >>> Proxy :: Proxy 3
+-- Proxy
+ test/testdata/TCPP.hs view
@@ -0,0 +1,19 @@+{-# LANGUAGE CPP #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RecordWildCards #-}
+
+module TCPP where
+
+-- >>> y
+y :: Integer
+y = 11
+
+#define ALL
+
+#ifdef ALL
+-- >>> 3+y
+#else
+-- >>> 5+y
+#endif
+
+-- >>> 2+y
+ test/testdata/TCPP.hs.expected view
@@ -0,0 +1,22 @@+{-# LANGUAGE CPP #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RecordWildCards #-}
+
+module TCPP where
+
+-- >>> y
+-- 11
+y :: Integer
+y = 11
+
+#define ALL
+
+#ifdef ALL
+-- >>> 3+y
+-- 14
+#else
+-- >>> 5+y
+#endif
+
+-- >>> 2+y
+-- 13
+ test/testdata/TCompare.hs view
@@ -0,0 +1,12 @@+module TCompare where
+
+-- In plain comments, the previous result of an evaluation is simply replaced
+
+-- >>> 2+2
+-- 5
+
+-- In Haddock comments, the previous result of an evaluation is compared with the new one
+-- and difference are displayed
+
+-- | >>> 2+2
+-- 5
+ test/testdata/TCompare.hs.expected view
@@ -0,0 +1,13 @@+module TCompare where
+
+-- In plain comments, the previous result of an evaluation is simply replaced
+
+-- >>> 2+2
+-- 4
+
+-- In Haddock comments, the previous result of an evaluation is compared with the new one
+-- and difference are displayed
+
+-- | >>> 2+2
+-- WAS 5
+-- NOW 4
+ test/testdata/TEndingMulti.hs view
@@ -0,0 +1,6 @@+module TEndingMulti where
+
+-- Now trailing doctest is allowed:
+
+{- >>> 42
+>>> 54-}
+ test/testdata/TEndingMulti.hs.expected view
@@ -0,0 +1,9 @@+module TEndingMulti where
+
+-- Now trailing doctest is allowed:
+
+{- >>> 42
+>>> 54
+42
+54
+-}
+ test/testdata/TFlags.hs view
@@ -0,0 +1,68 @@+-- Support for language options
+
+{-# LANGUAGE ScopedTypeVariables #-}
+module TFlags where
+
+-- Language options set in the module source (ScopedTypeVariables)
+-- also apply to tests so this works fine
+-- >>> f = (\(c::Char) -> [c])
+
+{- Multiple options can be set with a single `:set`
+
+>>> :set -XMultiParamTypeClasses -XFlexibleInstances
+>>> class Z a b c
+-}
+
+{-
+
+Options apply only in the section where they are defined (unless they are in the setup section), so this will fail:
+
+>>> class L a b c
+-}
+
+
+{-
+Options apply to all tests in the same section after their declaration.
+
+Not set yet:
+
+>>> class D
+
+Now it works:
+
+>>>:set -XMultiParamTypeClasses
+>>> class C
+
+It still works
+
+>>> class F
+-}
+
+{- Now -package flag is handled correctly:
+
+>>> :set -package ghc-prim
+>>> import GHC.Prim
+
+-}
+
+{- -fprint-* families
+
+>>> import Data.Proxy
+>>> :set -XPolyKinds
+>>> :t Proxy
+Proxy :: forall k (t :: k). Proxy t
+
+>>> :set -fprint-explicit-foralls
+>>> :t Proxy
+Proxy :: forall {k} {t :: k}. Proxy t
+-}
+
+{- Invalid option/flags are reported, but valid ones will be reflected
+
+>>> :set -XRank2Types -XAbsent -XDatatypeContexts -XWrong -fprint-nothing-at-all
+
+Still, Rank2Types is enabled, as in GHCi:
+
+>>> f = const 42 :: (forall x. x) -> Int
+>>> f undefined
+-}
+ test/testdata/TFlags.hs.expected view
@@ -0,0 +1,76 @@+-- Support for language options
+
+{-# LANGUAGE ScopedTypeVariables #-}
+module TFlags where
+
+-- Language options set in the module source (ScopedTypeVariables)
+-- also apply to tests so this works fine
+-- >>> f = (\(c::Char) -> [c])
+
+{- Multiple options can be set with a single `:set`
+
+>>> :set -XMultiParamTypeClasses -XFlexibleInstances
+>>> class Z a b c
+-}
+
+{-
+
+Options apply only in the section where they are defined (unless they are in the setup section), so this will fail:
+
+>>> class L a b c
+Too many parameters for class 'L'
+(Enable MultiParamTypeClasses to allow multi-parameter classes)
+-}
+
+
+{-
+Options apply to all tests in the same section after their declaration.
+
+Not set yet:
+
+>>> class D
+No parameters for class 'D'
+(Enable MultiParamTypeClasses to allow no-parameter classes)
+
+Now it works:
+
+>>>:set -XMultiParamTypeClasses
+>>> class C
+
+It still works
+
+>>> class F
+-}
+
+{- Now -package flag is handled correctly:
+
+>>> :set -package ghc-prim
+>>> import GHC.Prim
+
+-}
+
+{- -fprint-* families
+
+>>> import Data.Proxy
+>>> :set -XPolyKinds
+>>> :t Proxy
+Proxy :: forall k (t :: k). Proxy t
+
+>>> :set -fprint-explicit-foralls
+>>> :t Proxy
+Proxy :: forall {k} {t :: k}. Proxy t
+-}
+
+{- Invalid option/flags are reported, but valid ones will be reflected
+
+>>> :set -XRank2Types -XAbsent -XDatatypeContexts -XWrong -fprint-nothing-at-all
+<interactive>: warning:
+    -XDatatypeContexts is deprecated: It was widely considered a misfeature, and has been removed from the Haskell language.
+Some flags have not been recognized: -XAbsent, -XWrong, -fprint-nothing-at-all
+
+Still, Rank2Types is enabled, as in GHCi:
+
+>>> f = const 42 :: (forall x. x) -> Int
+>>> f undefined
+42
+-}
+ test/testdata/THaddock.hs view
@@ -0,0 +1,35 @@+{- |
+Tests in plain comments in both single line or multi line format, both forward and backward.
+Tests are ignored if:
+    * do not start on the first column (in Ordinary Haskell)
+    * do not start on the first or second column (in Literate Haskell)
+-}
+module THaddock () where
+
+{- ORMOLU_DISABLE -}
+
+-- | Single line comment
+-- >>> "a"++"b"
+
+{- | Multi line comment
+
+>>> "b"++"c"
+-}
+
+double a = a + a
+-- ^ Single line backward comments
+-- >>> double 11
+
+twice :: [a] -> [a]
+twice a = a ++ a
+{- ^ Multi-line backward comments
+>>> twice "ABC"
+-}
+
+{- | >>> 2+five
+
+   ^-- This works, as it starts at the first column after the header.
+
+     >>> IGNORED as it does not start on the first column
+-}
+five = 5
+ test/testdata/THaddock.hs.expected view
@@ -0,0 +1,40 @@+{- |
+Tests in plain comments in both single line or multi line format, both forward and backward.
+Tests are ignored if:
+    * do not start on the first column (in Ordinary Haskell)
+    * do not start on the first or second column (in Literate Haskell)
+-}
+module THaddock () where
+
+{- ORMOLU_DISABLE -}
+
+-- | Single line comment
+-- >>> "a"++"b"
+-- "ab"
+
+{- | Multi line comment
+
+>>> "b"++"c"
+"bc"
+-}
+
+double a = a + a
+-- ^ Single line backward comments
+-- >>> double 11
+-- 22
+
+twice :: [a] -> [a]
+twice a = a ++ a
+{- ^ Multi-line backward comments
+>>> twice "ABC"
+"ABCABC"
+-}
+
+{- | >>> 2+five
+7
+
+   ^-- This works, as it starts at the first column after the header.
+
+     >>> IGNORED as it does not start on the first column
+-}
+five = 5
+ test/testdata/TIO.hs view
@@ -0,0 +1,9 @@+-- IO expressions are supported, stdout/stderr output is ignored
+module TIO where
+
+{-
+Does not capture stdout, returns value.
+
+>>> print "ABC" >> return "XYZ"
+"XYZ"
+-}
+ test/testdata/TIO.hs.expected view
@@ -0,0 +1,9 @@+-- IO expressions are supported, stdout/stderr output is ignored
+module TIO where
+
+{-
+Does not capture stdout, returns value.
+
+>>> print "ABC" >> return "XYZ"
+"XYZ"
+-}
+ test/testdata/TIt.hs view
@@ -0,0 +1,7 @@+module TIt where
+
+-- >>> "test"
+-- >>> it
+
+-- >>> pure "test2"
+-- >>> it
+ test/testdata/TIt.hs.expected view
@@ -0,0 +1,11 @@+module TIt where
+
+-- >>> "test"
+-- >>> it
+-- "test"
+-- "test"
+
+-- >>> pure "test2"
+-- >>> it
+-- "test2"
+-- "test2"
+ test/testdata/TLHS.lhs view
@@ -0,0 +1,22 @@+An example LHS
+
+> {-# LANGUAGE OverloadedStrings #-}
+
+> module TLHS where
+
+More comments, here.
+
+> -- | >>> 2+2
+
+> -- | >>> x+a
+
+> {- |
+> >>> 5+5
+> 11
+> -}
+> x :: Integer
+> x = 3
+
+> a :: Integer
+> a = 11
+
+ test/testdata/TLHS.lhs.expected view
@@ -0,0 +1,25 @@+An example LHS
+
+> {-# LANGUAGE OverloadedStrings #-}
+
+> module TLHS where
+
+More comments, here.
+
+> -- | >>> 2+2
+> -- 4
+
+> -- | >>> x+a
+> -- 14
+
+> {- |
+> >>> 5+5
+> WAS 11
+> NOW 10
+> -}
+> x :: Integer
+> x = 3
+
+> a :: Integer
+> a = 11
+
+ test/testdata/TLHSLaTeX.lhs view
@@ -0,0 +1,16 @@+\documentstyle{article}
+
+\begin{document}
+
+\section{Introduction}
+
+This is a trivial program that prints the first 20 factorials.
+
+\begin{code}
+module TLHSLaTeX where
+
+-- >>> prod
+prod =  [ (n, product [1..n]) | n <- [1..3]]
+\end{code}
+
+\end{document}
+ test/testdata/TLHSLateX.lhs.expected view
@@ -0,0 +1,17 @@+\documentstyle{article}
+
+\begin{document}
+
+\section{Introduction}
+
+This is a trivial program that prints the first 20 factorials.
+
+\begin{code}
+module TLHSLaTeX where
+
+-- >>> prod
+-- [(1,1),(2,2),(3,6)]
+prod =  [ (n, product [1..n]) | n <- [1..3]]
+\end{code}
+
+\end{document}
+ test/testdata/TLanguageOptionsTupleSections.hs view
@@ -0,0 +1,9 @@+-- This works fine: {-# LANGUAGE TupleSections #-}
+module TLanguageOptionsTupleSection where
+
+-- Why oh why is this not working?
+-- What is special about TupleSections?
+-- >>> :set -XTupleSections
+-- >>> ("a",) "b"
+-- ("a","b")
+
+ test/testdata/TLanguageOptionsTupleSections.hs.expected view
@@ -0,0 +1,8 @@+-- This works fine: {-# LANGUAGE TupleSections #-}
+module TLanguageOptionsTupleSection where
+
+-- Why oh why is this not working?
+-- What is special about TupleSections?
+-- >>> :set -XTupleSections
+-- >>> ("a",) "b"
+-- ("a","b")
+ test/testdata/TLastLine.hs view
@@ -0,0 +1,3 @@+module TLastLine where
+
+-- >>> take 3 [1..]
+ test/testdata/TLastLine.hs.expected view
@@ -0,0 +1,4 @@+module TLastLine where
+
+-- >>> take 3 [1..]
+-- [1,2,3]
+ test/testdata/TLocalImport.hs view
@@ -0,0 +1,5 @@+module TLocalImport where
+
+import qualified Util
+
+-- >>> Util.tst 11 11
+ test/testdata/TLocalImport.hs.expected view
@@ -0,0 +1,6 @@+module TLocalImport where
+
+import qualified Util
+
+-- >>> Util.tst 11 11
+-- True
+ test/testdata/TLocalImportInTest.hs view
@@ -0,0 +1,4 @@+module TLocalImportInTest where
+
+-- >>> import qualified Util
+-- >>> Util.a
+ test/testdata/TLocalImportInTest.hs.expected view
@@ -0,0 +1,5 @@+module TLocalImportInTest where
+
+-- >>> import qualified Util
+-- >>> Util.a
+-- 'a'
+ test/testdata/TMulti.hs view
@@ -0,0 +1,19 @@+{- | Multi line comments are parsed correctly.
+-}
+module TMulti () where
+
+{- ORMOLU_DISABLE -}
+
+-- this should work fine if previous multi comment is parsed correctly
+-- >>> "a"++"b"
+
+  {- >>> 3+3
+    -}
+
+-- this should work fine if previous multi comment is parsed correctly
+-- >>> "a"++"b"
+
+  {-| >>> "NOT IGNORED" -}
+
+-- this should work fine if previous multi comment is parsed correctly
+-- >>> "a"++"b"
+ test/testdata/TMulti.hs.expected view
@@ -0,0 +1,25 @@+{- | Multi line comments are parsed correctly.
+-}
+module TMulti () where
+
+{- ORMOLU_DISABLE -}
+
+-- this should work fine if previous multi comment is parsed correctly
+-- >>> "a"++"b"
+-- "ab"
+
+  {- >>> 3+3
+6
+    -}
+
+-- this should work fine if previous multi comment is parsed correctly
+-- >>> "a"++"b"
+-- "ab"
+
+  {-| >>> "NOT IGNORED" 
+"NOT IGNORED"
+-}
+
+-- this should work fine if previous multi comment is parsed correctly
+-- >>> "a"++"b"
+-- "ab"
+ test/testdata/TNested.hs view
@@ -0,0 +1,13 @@+module TNseted () where
+{-
+>>> 54
+{-
+Nested
+-}
+-}
+
+{-
+{-
+>>> 42
+-}
+-}
+ test/testdata/TNested.hs.expected view
@@ -0,0 +1,15 @@+module TNseted () where
+{-
+>>> 54
+54
+{-
+Nested
+-}
+-}
+
+{-
+{-
+>>> 42
+42
+-}
+-}
+ test/testdata/TNoImplicitPrelude.hs view
@@ -0,0 +1,10 @@+{-# LANGUAGE NoImplicitPrelude #-}
+
+module TNoImplicitPrelude where
+
+import Data.List (unwords)
+import Data.String (String)
+
+-- >>> unwords example
+example :: [String]
+example = ["This","is","an","example","of","evaluation"]
+ test/testdata/TNoImplicitPrelude.hs.expected view
@@ -0,0 +1,11 @@+{-# LANGUAGE NoImplicitPrelude #-}
+
+module TNoImplicitPrelude where
+
+import Data.List (unwords)
+import Data.String (String)
+
+-- >>> unwords example
+-- "This is an example of evaluation"
+example :: [String]
+example = ["This","is","an","example","of","evaluation"]
+ test/testdata/TPlainComment.hs view
@@ -0,0 +1,25 @@+{- |
+Tests in plain comments in both single line or multi line format.
+Tests are ignored if:
+    * do not start on the first column
+    * are in multi line comments that open and close on the same line
+-}
+module TPlain where
+
+{- ORMOLU_DISABLE -}
+
+-- Single line comment
+-- >>> "a"++"b"
+
+{- Multi line comment
+
+>>> "b"++"c"
+-}
+
+{- >>> 2+five
+
+   ^-- This works, as it starts at the first column after the header.
+
+     >>> IGNORED as it does not start on the first column
+-}
+five = 5
+ test/testdata/TPlainComment.hs.expected view
@@ -0,0 +1,28 @@+{- |
+Tests in plain comments in both single line or multi line format.
+Tests are ignored if:
+    * do not start on the first column
+    * are in multi line comments that open and close on the same line
+-}
+module TPlain where
+
+{- ORMOLU_DISABLE -}
+
+-- Single line comment
+-- >>> "a"++"b"
+-- "ab"
+
+{- Multi line comment
+
+>>> "b"++"c"
+"bc"
+-}
+
+{- >>> 2+five
+7
+
+   ^-- This works, as it starts at the first column after the header.
+
+     >>> IGNORED as it does not start on the first column
+-}
+five = 5
+ test/testdata/TPrelude.hs view
@@ -0,0 +1,10 @@+-- Prelude has no special treatment, it is imported as stated in the module.
+module TPrelude where
+
+import           Prelude hiding (foldr)
+
+-- >>> foldr (+) 10 [2,3,5]
+foldr :: (a -> z -> z) -> z -> [a] -> z
+foldr f z bs =
+   (foldl (\g a -> g . f a) id bs) z
+
+ test/testdata/TPrelude.hs.expected view
@@ -0,0 +1,11 @@+-- Prelude has no special treatment, it is imported as stated in the module.
+module TPrelude where
+
+import           Prelude hiding (foldr)
+
+-- >>> foldr (+) 10 [2,3,5]
+-- 20
+foldr :: (a -> z -> z) -> z -> [a] -> z
+foldr f z bs =
+   (foldl (\g a -> g . f a) id bs) z
+
+ test/testdata/TProperty.hs view
@@ -0,0 +1,5 @@+-- Support for property checking
+module TProperty where
+
+-- prop> \(l::[Bool]) -> reverse (reverse l) == l
+
+ test/testdata/TProperty.hs.expected view
@@ -0,0 +1,6 @@+-- Support for property checking
+module TProperty where
+
+-- prop> \(l::[Bool]) -> reverse (reverse l) == l
+-- +++ OK, passed 100 tests.
+
+ test/testdata/TSectionEval.hs view
@@ -0,0 +1,15 @@+-- Tests in the same document section are executed together
+module TSectionEval where
+
+{-
+>>> 1+1
+
+>>> 2+2
+-}
+
+{-
+>>> 3+3
+
+>>> 4+4
+-}
+
+ test/testdata/TSectionEval.hs.expected view
@@ -0,0 +1,17 @@+-- Tests in the same document section are executed together
+module TSectionEval where
+
+{-
+>>> 1+1
+2
+
+>>> 2+2
+4
+-}
+
+{-
+>>> 3+3
+
+>>> 4+4
+-}
+
+ test/testdata/TSetup.hs view
@@ -0,0 +1,10 @@+-- The setup section is executed before any other test
+module TSetup where
+
+-- $setup
+-- >>> x=11
+-- >>> y=22
+
+-- >>> x+y
+
+-- >>> x*y
+ test/testdata/TSetup.hs.expected view
@@ -0,0 +1,12 @@+-- The setup section is executed before any other test
+module TSetup where
+
+-- $setup
+-- >>> x=11
+-- >>> y=22
+
+-- >>> x+y
+-- 33
+
+-- >>> x*y
+-- 242
+ test/testdata/TUNPACK.hs view
@@ -0,0 +1,13 @@+{- | Won't panic on UNPACKs -}
+module TUNPACK (THStatus(..)) where
+
+type ByteString = String
+type BSEndo = ByteString -> ByteString
+type BSEndoList = [ByteString] -> [ByteString]
+
+data THStatus = THStatus
+    {-# UNPACK #-} !Int -- running total byte count
+    BSEndoList -- previously parsed lines
+    BSEndo -- bytestrings to be prepended
+
+-- >>> "Yay! UNPACK pragma didn't do bad things!"
+ test/testdata/TUNPACK.hs.expected view
@@ -0,0 +1,14 @@+{- | Won't panic on UNPACKs -}
+module TUNPACK (THStatus(..)) where
+
+type ByteString = String
+type BSEndo = ByteString -> ByteString
+type BSEndoList = [ByteString] -> [ByteString]
+
+data THStatus = THStatus
+    {-# UNPACK #-} !Int -- running total byte count
+    BSEndoList -- previously parsed lines
+    BSEndo -- bytestrings to be prepended
+
+-- >>> "Yay! UNPACK pragma didn't do bad things!"
+-- "Yay! UNPACK pragma didn't do bad things!"
+ test/testdata/Util.hs view
@@ -0,0 +1,13 @@+-- Used for testing local imports
+module Util
+  ( tst
+  , a
+  , b
+  )
+where
+
+tst a b = a == b
+
+a = 'a'
+
+b = 'b'
+ test/testdata/hie.yaml view
@@ -0,0 +1,4 @@+cradle:
+  cabal:
+    - path: "././"
+      component: "lib:test"
+ test/testdata/test.cabal view
@@ -0,0 +1,63 @@+name:                test
+version:             0.1.0.0
+-- synopsis:
+-- description:
+license:             BSD3
+author:              Author name here
+maintainer:          example@example.com
+copyright:           2017 Author name here
+category:            Web
+build-type:          Simple
+cabal-version:       >=1.10
+
+library
+  exposed-modules:
+      T1
+      T2
+      T3
+      T4
+      T5
+      T6
+      T7
+      T8
+      T9
+      T10
+      T11
+      T12
+      T13
+      T14
+      T15
+      T16
+      T17
+      T18
+      T19
+      T20
+      T21
+      T22
+      T23
+      T24
+      T25
+      T26
+      T27
+      TEndingMulti
+      TMulti
+      TPlainComment
+      THaddock
+      TCompare
+      TLocalImport
+      TLocalImportInTest
+      TFlags
+      TLanguageOptionsTupleSections
+      TIO
+      TProperty
+      TPrelude
+      TCPP
+      TLHS
+      TSetup
+      Util
+      TNested
+
+  build-depends:       base >= 4.7 && < 5, QuickCheck
+  default-language:    Haskell2010
+  ghc-options:         -Wall -fwarn-unused-imports
+