hls-eval-plugin 1.1.0.0 → 1.1.1.0
raw patch · 163 files changed
+4008/−4049 lines, 163 filesdep +ghc-api-compatdep −lsp-testdep −shakedep ~ghcidenew-uploaderPVP ok
version bump matches the API change (PVP)
Dependencies added: ghc-api-compat
Dependencies removed: lsp-test, shake
Dependency ranges changed: ghcide
API changes (from Hackage documentation)
Files
- LICENSE +201/−201
- README.md +332/−332
- hls-eval-plugin.cabal +110/−112
- src/Ide/Plugin/Eval.hs +25/−28
- src/Ide/Plugin/Eval/Code.hs +131/−131
- src/Ide/Plugin/Eval/CodeLens.hs +750/−711
- src/Ide/Plugin/Eval/GHC.hs +171/−174
- src/Ide/Plugin/Eval/Parse/Comments.hs +571/−571
- src/Ide/Plugin/Eval/Parse/Option.hs +40/−40
- src/Ide/Plugin/Eval/Types.hs +162/−162
- src/Ide/Plugin/Eval/Util.hs +106/−105
- test/Main.hs +137/−218
- test/testdata/T1.expected.hs +8/−0
- test/testdata/T1.hs +7/−7
- test/testdata/T1.hs.expected +0/−8
- test/testdata/T10.expected.hs +11/−0
- test/testdata/T10.hs +9/−9
- test/testdata/T10.hs.expected +0/−11
- test/testdata/T11.expected.hs +4/−0
- test/testdata/T11.hs +3/−3
- test/testdata/T11.hs.expected +0/−4
- test/testdata/T12.expected.hs +10/−0
- test/testdata/T12.hs +9/−9
- test/testdata/T12.hs.expected +0/−10
- test/testdata/T13.expected.hs +4/−0
- test/testdata/T13.hs +3/−3
- test/testdata/T13.hs.expected +0/−4
- test/testdata/T14.expected.hs +8/−0
- test/testdata/T14.hs +7/−7
- test/testdata/T14.hs.expected +0/−8
- test/testdata/T15.expected.hs +8/−0
- test/testdata/T15.hs +7/−7
- test/testdata/T15.hs.expected +0/−8
- test/testdata/T16.expected.hs +4/−0
- test/testdata/T16.hs +3/−3
- test/testdata/T16.hs.expected +0/−4
- test/testdata/T17.expected.hs +4/−0
- test/testdata/T17.hs +3/−3
- test/testdata/T17.hs.expected +0/−4
- test/testdata/T18.expected.hs +5/−0
- test/testdata/T18.hs +4/−4
- test/testdata/T18.hs.expected +0/−5
- test/testdata/T19.expected.hs +7/−0
- test/testdata/T19.hs +6/−6
- test/testdata/T19.hs.expected +0/−7
- test/testdata/T2.expected.hs +8/−0
- test/testdata/T2.hs +8/−8
- test/testdata/T2.hs.expected +0/−8
- test/testdata/T20.expected.hs +7/−0
- test/testdata/T20.hs +6/−6
- test/testdata/T20.hs.expected +0/−7
- test/testdata/T21.expected.hs +16/−0
- test/testdata/T21.hs +12/−12
- test/testdata/T21.hs.expected +0/−16
- test/testdata/T22.expected.hs +10/−0
- test/testdata/T22.hs +9/−9
- test/testdata/T22.hs.expected +0/−10
- test/testdata/T23.expected.hs +15/−0
- test/testdata/T23.hs +12/−12
- test/testdata/T23.hs.expected +0/−15
- test/testdata/T24.expected.hs +32/−0
- test/testdata/T24.hs +14/−14
- test/testdata/T24.hs.expected +0/−32
- test/testdata/T25.expected.hs +28/−0
- test/testdata/T25.hs +14/−14
- test/testdata/T25.hs.expected +0/−28
- test/testdata/T26.expected.hs +5/−0
- test/testdata/T26.hs +4/−4
- test/testdata/T26.hs.expected +0/−5
- test/testdata/T27.expected.hs +5/−0
- test/testdata/T27.hs +4/−4
- test/testdata/T27.hs.expected +0/−5
- test/testdata/T3.expected.hs +8/−0
- test/testdata/T3.hs +7/−7
- test/testdata/T3.hs.expected +0/−8
- test/testdata/T4.expected.hs +10/−0
- test/testdata/T4.hs +9/−9
- test/testdata/T4.hs.expected +0/−10
- test/testdata/T5.expected.hs +6/−0
- test/testdata/T5.hs +6/−6
- test/testdata/T5.hs.expected +0/−6
- test/testdata/T6.expected.hs +9/−0
- test/testdata/T6.hs +9/−9
- test/testdata/T6.hs.expected +0/−9
- test/testdata/T7.expected.hs +10/−0
- test/testdata/T7.hs +10/−10
- test/testdata/T7.hs.expected +0/−10
- test/testdata/T8.expected.hs +14/−0
- test/testdata/T8.hs +10/−10
- test/testdata/T8.hs.expected +0/−14
- test/testdata/T9.expected.hs +8/−0
- test/testdata/T9.hs +7/−7
- test/testdata/T9.hs.expected +0/−8
- test/testdata/TCPP.expected.hs +22/−0
- test/testdata/TCPP.hs +19/−19
- test/testdata/TCPP.hs.expected +0/−22
- test/testdata/TCompare.expected.hs +13/−0
- test/testdata/TCompare.hs +12/−12
- test/testdata/TCompare.hs.expected +0/−13
- test/testdata/TEndingMulti.expected.hs +9/−0
- test/testdata/TEndingMulti.hs +6/−6
- test/testdata/TEndingMulti.hs.expected +0/−9
- test/testdata/TFlags.expected.hs +76/−0
- test/testdata/TFlags.hs +68/−68
- test/testdata/TFlags.hs.expected +0/−76
- test/testdata/THaddock.expected.hs +42/−0
- test/testdata/THaddock.hs +37/−35
- test/testdata/THaddock.hs.expected +0/−40
- test/testdata/TIO.expected.hs +9/−0
- test/testdata/TIO.hs +9/−9
- test/testdata/TIO.hs.expected +0/−9
- test/testdata/TIt.expected.hs +11/−0
- test/testdata/TIt.hs +7/−7
- test/testdata/TIt.hs.expected +0/−11
- test/testdata/TLHS.expected.lhs +25/−0
- test/testdata/TLHS.lhs +22/−22
- test/testdata/TLHS.lhs.expected +0/−25
- test/testdata/TLHSLaTeX.expected.lhs +17/−0
- test/testdata/TLHSLaTeX.lhs +16/−16
- test/testdata/TLHSLateX.lhs.expected +0/−17
- test/testdata/TLanguageOptionsTupleSections.expected.hs +9/−0
- test/testdata/TLanguageOptionsTupleSections.hs +9/−9
- test/testdata/TLanguageOptionsTupleSections.hs.expected +0/−8
- test/testdata/TLastLine.expected.hs +4/−0
- test/testdata/TLastLine.hs +2/−2
- test/testdata/TLastLine.hs.expected +0/−4
- test/testdata/TLocalImport.expected.hs +6/−0
- test/testdata/TLocalImport.hs +5/−5
- test/testdata/TLocalImport.hs.expected +0/−6
- test/testdata/TLocalImportInTest.expected.hs +5/−0
- test/testdata/TLocalImportInTest.hs +4/−4
- test/testdata/TLocalImportInTest.hs.expected +0/−5
- test/testdata/TMulti.expected.hs +25/−0
- test/testdata/TMulti.hs +19/−19
- test/testdata/TMulti.hs.expected +0/−25
- test/testdata/TNested.expected.hs +15/−0
- test/testdata/TNested.hs +13/−13
- test/testdata/TNested.hs.expected +0/−15
- test/testdata/TNoImplicitPrelude.expected.hs +11/−0
- test/testdata/TNoImplicitPrelude.hs +10/−10
- test/testdata/TNoImplicitPrelude.hs.expected +0/−11
- test/testdata/TPlainComment.expected.hs +29/−0
- test/testdata/TPlainComment.hs +26/−25
- test/testdata/TPlainComment.hs.expected +0/−28
- test/testdata/TPrelude.expected.hs +11/−0
- test/testdata/TPrelude.hs +10/−10
- test/testdata/TPrelude.hs.expected +0/−11
- test/testdata/TProperty.expected.hs +6/−0
- test/testdata/TProperty.hs +5/−5
- test/testdata/TProperty.hs.expected +0/−6
- test/testdata/TSectionEval.expected.hs +17/−0
- test/testdata/TSectionEval.hs +15/−15
- test/testdata/TSectionEval.hs.expected +0/−17
- test/testdata/TSetup.expected.hs +12/−0
- test/testdata/TSetup.hs +10/−10
- test/testdata/TSetup.hs.expected +0/−12
- test/testdata/TUNPACK.expected.hs +14/−0
- test/testdata/TUNPACK.hs +13/−13
- test/testdata/TUNPACK.hs.expected +0/−14
- test/testdata/Util.hs +13/−13
- test/testdata/cabal.project +1/−0
- test/testdata/hie.yaml +4/−4
- test/testdata/test.cabal +63/−63
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 - - - -# 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++++# 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,112 +1,110 @@-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 +cabal-version: 2.4+name: hls-eval-plugin+version: 1.1.1.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/*.hs+ test/testdata/*.lhs+ test/testdata/*.yaml+ test/testdata/cabal.project++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-api-compat+ , ghc-boot-th+ , ghc-paths+ , ghcide >=1.2 && <1.5+ , hashable+ , hls-plugin-api ^>=1.1+ , lens+ , lsp+ , lsp-types+ , megaparsec >=9.0+ , mtl+ , parser-combinators+ , pretty-simple+ , QuickCheck+ , safe-exceptions+ , 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-types
src/Ide/Plugin/Eval.hs view
@@ -1,28 +1,25 @@-{-# 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,131 +1,131 @@-{-# 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 - ] +{-# 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,711 +1,750 @@-{-# 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 +{-# LANGUAGE CPP #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE ExtendedDefaultRules #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE NoMonomorphismRestriction #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE PatternSynonyms #-}+{-# 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 (UnhelpfulSpan),+ moduleName,+ 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),+ 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,+ parseDynamicFlagsCmdLine,+ targetPlatform, 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))+++#if MIN_VERSION_ghc(9,0,0)+import GHC.Parser.Annotation (ApiAnns (apiAnnComments))+#else+import GhcPlugins (interpWays, updateWays,+ wayGeneralFlags,+ wayUnsetGeneralFlags)+#endif++#if MIN_VERSION_ghc(9,0,0)+pattern RealSrcSpanAlready :: SrcLoc.RealSrcSpan -> SrcLoc.RealSrcSpan+pattern RealSrcSpanAlready x = x+#else+apiAnnComments :: SrcLoc.ApiAnns -> Map.Map SrcSpan [SrcLoc.Located AnnotationComment]+apiAnnComments = snd++pattern RealSrcSpanAlready :: SrcLoc.RealSrcSpan -> SrcSpan+pattern RealSrcSpanAlready x = SrcLoc.RealSrcSpan x+#endif+++{- | 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 (RealSrcSpanAlready 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+ )+ $ apiAnnComments pm_annotations+ dbg "excluded comments" $ show $ DL.toList $+ foldMap+ (foldMap $ \(L a b) ->+ case b of+ AnnLineComment{} -> mempty+ AnnBlockComment{} -> mempty+ _ -> DL.singleton (a, b)+ )+ $ apiAnnComments 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)+#if MIN_VERSION_ghc(9,0,0)+ { unitState =+ unitState+ df+ , unitDatabases =+ unitDatabases+ df+ , packageFlags =+ packageFlags+ df+ , useColor = Never+ , canUseColor = False+ }+#else+ { pkgState =+ pkgState+ df+ , pkgDatabase =+ pkgDatabase+ df+ , packageFlags =+ packageFlags+ df+ , useColor = Never+ , canUseColor = False+ }+#endif++ -- set up a custom log action+#if MIN_VERSION_ghc(9,0,0)+ setLogAction $ \_df _wr _sev _span _doc ->+ defaultLogActionHPutStrDoc _df logHandle _doc+#else+ setLogAction $ \_df _wr _sev _span _style _doc ->+ defaultLogActionHPutStrDoc _df logHandle _doc _style+#endif++ -- 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,174 +1,171 @@-{-# 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 +{-# 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 (fsLit, hsc_IC, pprHsString)+import HscTypes (InteractiveContext (ic_dflags))+import Ide.Plugin.Eval.Util (asS, gStrictTry)+import qualified Lexer+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,571 +1,571 @@-{-# 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 +{-# 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 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) +{-# 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 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) +{-# 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,105 +1,106 @@-{-# 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 +{-# 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 Development.IDE.GHC.Compat (gcatch)+import Exception (ExceptionMonad, SomeException (..),+ evaluate)+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
@@ -1,218 +1,137 @@-{-# 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" +{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}+{-# 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 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 testDataDir $ do+ doc <- openDoc "T1.hs" "haskell"+ lenses <- getCodeLenses doc+ liftIO $ map (preview $ command . _Just . title) lenses @?= [Just "Evaluate..."]+ , testCase "Produces Refresh code lenses" $+ runSessionWithServer evalPlugin testDataDir $ do+ doc <- openDoc "T2.hs" "haskell"+ lenses <- getCodeLenses doc+ liftIO $ map (preview $ command . _Just . title) lenses @?= [Just "Refresh..."]+ , testCase "Code lenses have ranges" $+ runSessionWithServer evalPlugin testDataDir $ do+ doc <- openDoc "T1.hs" "haskell"+ lenses <- getCodeLenses 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 testDataDir $ do+ doc <- openDoc "T3.hs" "haskell"+ lenses <- getCodeLenses doc+ liftIO $ map (view range) lenses @?= [Range (Position 3 0) (Position 5 0)]+ , testCase "Executed expressions range covers only the expression" $ do+ runSessionWithServer evalPlugin testDataDir $ do+ doc <- openDoc "T2.hs" "haskell"+ lenses <- getCodeLenses doc+ liftIO $ map (view range) lenses @?= [Range (Position 4 0) (Position 5 0)]++ , goldenWithEval "Evaluation of expressions" "T1" "hs"+ , goldenWithEval "Reevaluation of expressions" "T2" "hs"+ , goldenWithEval "Evaluation of expressions w/ imports" "T3" "hs"+ , goldenWithEval "Evaluation of expressions w/ lets" "T4" "hs"+ , goldenWithEval "Refresh an evaluation" "T5" "hs"+ , goldenWithEval "Refresh an evaluation w/ lets" "T6" "hs"+ , goldenWithEval "Refresh a multiline evaluation" "T7" "hs"+ , goldenWithEval "Semantic and Lexical errors are reported" "T8" "hs"+ , goldenWithEval "Applies file LANGUAGE extensions" "T9" "hs"+ , goldenWithEval "Evaluate a type with :kind!" "T10" "hs"+ , goldenWithEval "Reports an error for an incorrect type with :kind!" "T11" "hs"+ , goldenWithEval "Shows a kind with :kind" "T12" "hs"+ , goldenWithEval "Reports an error for an incorrect type with :kind" "T13" "hs"+ , goldenWithEval "Returns a fully-instantiated type for :type" "T14" "hs"+ , goldenWithEval "Returns an uninstantiated type for :type +v, admitting multiple whitespaces around arguments" "T15" "hs"+ , goldenWithEval "Returns defaulted type for :type +d, admitting multiple whitespaces around arguments" "T16" "hs"+ , goldenWithEval ":type reports an error when given with unknown +x option" "T17" "hs"+ , goldenWithEval "Reports an error when given with unknown command" "T18" "hs"+ , goldenWithEval "Returns defaulted type for :type +d reflecting the default declaration specified in the >>> prompt" "T19" "hs"+ , expectFailBecause "known issue - see a note in P.R. #361" $+ goldenWithEval ":type +d reflects the `default' declaration of the module" "T20" "hs"+ , goldenWithEval ":type handles a multilined result properly" "T21" "hs"+ , goldenWithEval ":t behaves exactly the same as :type" "T22" "hs"+ , goldenWithEval ":type does \"dovetails\" for short identifiers" "T23" "hs"+ , goldenWithEval ":kind! treats a multilined result properly" "T24" "hs"+ , goldenWithEval ":kind treats a multilined result properly" "T25" "hs"+ , goldenWithEval "local imports" "T26" "hs"+ , goldenWithEval "Preserves one empty comment line after prompt" "T27" "hs"+ , goldenWithEval "Multi line comments" "TMulti" "hs"+ , goldenWithEval "Multi line comments, with the last test line ends without newline" "TEndingMulti" "hs"+ , goldenWithEval "Evaluate expressions in Plain comments in both single line and multi line format" "TPlainComment" "hs"+ , goldenWithEval "Evaluate expressions in Haddock comments in both single line and multi line format" "THaddock" "hs"+ , goldenWithEval "Compare results (for Haddock tests only)" "TCompare" "hs"+ , goldenWithEval "Local Modules imports are accessible in a test" "TLocalImport" "hs"+ -- , goldenWithEval "Local Modules can be imported in a test" "TLocalImportInTest" "hs"+ , goldenWithEval "Setting language option TupleSections" "TLanguageOptionsTupleSections" "hs"+ , goldenWithEval ":set accepts ghci flags" "TFlags" "hs"+ , goldenWithEval "IO expressions are supported, stdout/stderr output is ignored" "TIO" "hs"+ , goldenWithEval "Property checking" "TProperty" "hs"+ , goldenWithEval "Prelude has no special treatment, it is imported as stated in the module" "TPrelude" "hs"+ , goldenWithEval "Don't panic on {-# UNPACK #-} pragma" "TUNPACK" "hs"+ , goldenWithEval "Can handle eval inside nested comment properly" "TNested" "hs"+ , goldenWithEval "Test on last line insert results correctly" "TLastLine" "hs"+ , testGroup "with preprocessors"+ [ knownBrokenInEnv [HostOS Windows, GhcVer GHC84, GhcVer GHC86]+ "CPP eval on Windows and/or GHC <= 8.6 fails for some reasons" $+ goldenWithEval "CPP support" "TCPP" "hs"+ , knownBrokenForGhcVersions [GHC84, GHC86]+ "Preprocessor known to fail on GHC <= 8.6" $+ goldenWithEval "Literate Haskell Bird Style" "TLHS" "lhs"+ -- , goldenWithEval "Literate Haskell LaTeX Style" "TLHSLateX" "lhs"+ ]+ , goldenWithEval "Works with NoImplicitPrelude" "TNoImplicitPrelude" "hs"+ , goldenWithEval "Variable 'it' works" "TIt" "hs"+ ]++goldenWithEval :: TestName -> FilePath -> FilePath -> TestTree+goldenWithEval title path ext = goldenWithHaskellDoc evalPlugin title testDataDir path "expected" ext $ \doc -> do+ -- Execute lenses backwards, to avoid affecting their position in the source file+ codeLenses <- reverse <$> getCodeLenses 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]++actSectionId :: Command -> Int+actSectionId Command{_arguments = Just (List [fromJSON -> Success EvalParams{..}])} = evalId+actSectionId _ = error "Invalid CodeLens"++-- Execute command and wait for result+executeCmd :: Command -> Session ()+executeCmd cmd = do+ executeCommand cmd+ _ <- skipManyTill anyMessage (message SWorkspaceApplyEdit)+ -- liftIO $ print _resp+ pure ()++testDataDir :: FilePath+testDataDir = "test" </> "testdata"
+ test/testdata/T1.expected.hs 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/T1.hs view
@@ -1,7 +1,7 @@-module T1 where - -import Data.List (unwords) - --- >>> unwords example -example :: [String] -example = ["This","is","an","example","of","evaluation"] +module T1 where++import Data.List (unwords)++-- >>> unwords example+example :: [String]+example = ["This","is","an","example","of","evaluation"]
− test/testdata/T1.hs.expected
@@ -1,8 +0,0 @@-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.expected.hs 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/T10.hs view
@@ -1,9 +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 +{-# 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
@@ -1,11 +0,0 @@-{-# 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.expected.hs view
@@ -0,0 +1,4 @@+module T11 where++-- >>> :kind! a+-- Not in scope: type variable ‘a’
test/testdata/T11.hs view
@@ -1,3 +1,3 @@-module T11 where - --- >>> :kind! a +module T11 where++-- >>> :kind! a
− test/testdata/T11.hs.expected
@@ -1,4 +0,0 @@-module T11 where - --- >>> :kind! a --- Not in scope: type variable 'a'
+ test/testdata/T12.expected.hs 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/T12.hs view
@@ -1,9 +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 +{-# 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
@@ -1,10 +0,0 @@-{-# 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.expected.hs view
@@ -0,0 +1,4 @@+module T13 where++-- >>> :kind a+-- Not in scope: type variable ‘a’
test/testdata/T13.hs view
@@ -1,3 +1,3 @@-module T13 where - --- >>> :kind a +module T13 where++-- >>> :kind a
− test/testdata/T13.hs.expected
@@ -1,4 +0,0 @@-module T13 where - --- >>> :kind a --- Not in scope: type variable 'a'
+ test/testdata/T14.expected.hs 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/T14.hs view
@@ -1,7 +1,7 @@-{-# LANGUAGE TypeApplications #-} -module T14 where - -foo :: Show a => a -> String -foo = show - --- >>> :type foo @Int +{-# LANGUAGE TypeApplications #-}+module T14 where++foo :: Show a => a -> String+foo = show++-- >>> :type foo @Int
− test/testdata/T14.hs.expected
@@ -1,8 +0,0 @@-{-# LANGUAGE TypeApplications #-} -module T14 where - -foo :: Show a => a -> String -foo = show - --- >>> :type foo @Int --- foo @Int :: Int -> String
+ test/testdata/T15.expected.hs 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/T15.hs view
@@ -1,7 +1,7 @@-{-# LANGUAGE TypeApplications #-} -module T15 where - -foo :: Show a => a -> String -foo = show - --- >>> :type +v foo @Int +{-# LANGUAGE TypeApplications #-}+module T15 where++foo :: Show a => a -> String+foo = show++-- >>> :type +v foo @Int
− test/testdata/T15.hs.expected
@@ -1,8 +0,0 @@-{-# 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.expected.hs view
@@ -0,0 +1,4 @@+module T16 where++-- >>> :type +d 40+ 2 +-- 40+ 2 :: Integer
test/testdata/T16.hs view
@@ -1,3 +1,3 @@-module T16 where - --- >>> :type +d 40+ 2 +module T16 where++-- >>> :type +d 40+ 2
− test/testdata/T16.hs.expected
@@ -1,4 +0,0 @@-module T16 where - --- >>> :type +d 40+ 2 --- 40+ 2 :: Integer
+ test/testdata/T17.expected.hs view
@@ -0,0 +1,4 @@+module T17 where++-- >>> :type +no 42+-- parse error on input ‘+’
test/testdata/T17.hs view
@@ -1,3 +1,3 @@-module T17 where - --- >>> :type +no 42 +module T17 where++-- >>> :type +no 42
− test/testdata/T17.hs.expected
@@ -1,4 +0,0 @@-module T17 where - --- >>> :type +no 42 --- parse error on input '+'
+ test/testdata/T18.expected.hs view
@@ -0,0 +1,5 @@+{-# LANGUAGE TypeApplications #-}+module T18 where++-- >>> :noooop foo bar+-- unknown command 'noooop'
test/testdata/T18.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE TypeApplications #-} -module T18 where - --- >>> :noooop foo bar +{-# LANGUAGE TypeApplications #-}+module T18 where++-- >>> :noooop foo bar
− test/testdata/T18.hs.expected
@@ -1,5 +0,0 @@-{-# LANGUAGE TypeApplications #-} -module T18 where - --- >>> :noooop foo bar --- unknown command 'noooop'
+ test/testdata/T19.expected.hs 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/T19.hs view
@@ -1,6 +1,6 @@-module T19 where -import Data.Word (Word) -type W = Word - --- >>> default (Word) --- >>> :type +d 40+ 2 +module T19 where+import Data.Word (Word)+type W = Word++-- >>> default (Word)+-- >>> :type +d 40+ 2
− test/testdata/T19.hs.expected
@@ -1,7 +0,0 @@-module T19 where -import Data.Word (Word) -type W = Word - --- >>> default (Word) --- >>> :type +d 40+ 2 --- 40+ 2 :: Word
+ test/testdata/T2.expected.hs 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/T2.hs view
@@ -1,8 +1,8 @@-module T2 where - -import Data.List (unwords) - --- >>> unwords example --- "Stale output" -example :: [String] -example = ["This","is","an","example","of","evaluation"] +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
@@ -1,8 +0,0 @@-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.expected.hs view
@@ -0,0 +1,7 @@+module T20 where+import Data.Word (Word)++default (Word)++-- >>> :type +d 40+ 2 +-- 40+ 2 :: Word
test/testdata/T20.hs view
@@ -1,6 +1,6 @@-module T20 where -import Data.Word (Word) - -default (Word) - --- >>> :type +d 40+ 2 +module T20 where+import Data.Word (Word)++default (Word)++-- >>> :type +d 40+ 2
− test/testdata/T20.hs.expected
@@ -1,7 +0,0 @@-module T20 where -import Data.Word (Word) - -default (Word) - --- >>> :type +d 40+ 2 --- 40+ 2 :: Word
+ test/testdata/T21.expected.hs 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/T21.hs view
@@ -1,12 +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 +{-# 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
@@ -1,16 +0,0 @@-{-# 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.expected.hs 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/T22.hs view
@@ -1,9 +1,9 @@-module T22 where -import Data.Proxy (Proxy (..)) -import GHC.TypeNats (KnownNat) -import Type.Reflection (Typeable) - -f :: Integer -f = 32 - --- >>> :t f +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
@@ -1,10 +0,0 @@-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.expected.hs 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/T23.hs view
@@ -1,12 +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 +{-# 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
@@ -1,15 +0,0 @@-{-# 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.expected.hs 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/T24.hs view
@@ -1,14 +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))))))))))))) +{-# 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
@@ -1,32 +0,0 @@-{-# 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.expected.hs 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/T25.hs view
@@ -1,14 +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))))))))))))) +{-# 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
@@ -1,28 +0,0 @@-{-# 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.expected.hs view
@@ -0,0 +1,5 @@+module T26 where+import Util ()++-- >>> "hello"+-- "hello"
test/testdata/T26.hs view
@@ -1,4 +1,4 @@-module T26 where -import T25 () - --- >>> "hello" +module T26 where+import Util ()++-- >>> "hello"
− test/testdata/T26.hs.expected
@@ -1,5 +0,0 @@-module T26 where -import T25 () - --- >>> "hello" --- "hello"
+ test/testdata/T27.expected.hs view
@@ -0,0 +1,5 @@+module T27 where++-- >>> ()+-- ()+--
test/testdata/T27.hs view
@@ -1,4 +1,4 @@-module T27 where - --- >>> () --- +module T27 where++-- >>> ()+--
− test/testdata/T27.hs.expected
@@ -1,5 +0,0 @@-module T27 where - --- >>> () --- () ---
+ test/testdata/T3.expected.hs 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/T3.hs view
@@ -1,7 +1,7 @@-module T3 where - - --- >>> import Data.List (unwords) --- >>> unwords example -example :: [String] -example = ["This","is","an","example","of","evaluation"] +module T3 where+++-- >>> import Data.List (unwords)+-- >>> unwords example+example :: [String]+example = ["This","is","an","example","of","evaluation"]
− test/testdata/T3.hs.expected
@@ -1,8 +0,0 @@-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.expected.hs 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/T4.hs view
@@ -1,9 +1,9 @@-module T4 where - -import Data.List (unwords) - --- >>> let evaluation = " evaluation" --- >>> unwords example ++ evaluation -example :: [String] -example = ["This","is","an","example","of"] - +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
@@ -1,10 +0,0 @@-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.expected.hs 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/T5.hs view
@@ -1,6 +1,6 @@-module T5 where - --- >>> unwords example --- "This is a stale example of evaluation" -example :: [String] -example = ["This","is","an","example","of","evaluation"] +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
@@ -1,6 +0,0 @@-module T5 where - --- >>> unwords example --- "This is an example of evaluation" -example :: [String] -example = ["This","is","an","example","of","evaluation"]
+ test/testdata/T6.expected.hs 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/T6.hs view
@@ -1,9 +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"] +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
@@ -1,9 +0,0 @@-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.expected.hs 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/T7.hs view
@@ -1,10 +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"] +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
@@ -1,10 +0,0 @@-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.expected.hs 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/T8.hs view
@@ -1,10 +1,10 @@--- Semantic and Lexical errors are reported -module T8 where - --- >>> noFunctionWithThisName - --- >>> "a" + "bc" - --- >>> " - --- >>> 3 `div` 0 +-- Semantic and Lexical errors are reported+module T8 where++-- >>> noFunctionWithThisName++-- >>> "a" + "bc"++-- >>> "++-- >>> 3 `div` 0
− test/testdata/T8.hs.expected
@@ -1,14 +0,0 @@--- 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.expected.hs view
@@ -0,0 +1,8 @@+{-# LANGUAGE DataKinds #-}+module T9 where+import Data.Proxy (Proxy(..))++type P = Proxy++-- >>> Proxy :: Proxy 3+-- Proxy
test/testdata/T9.hs view
@@ -1,7 +1,7 @@-{-# LANGUAGE DataKinds #-} -module T9 where -import Data.Proxy (Proxy(..)) - -type P = Proxy - --- >>> Proxy :: Proxy 3 +{-# LANGUAGE DataKinds #-}+module T9 where+import Data.Proxy (Proxy(..))++type P = Proxy++-- >>> Proxy :: Proxy 3
− test/testdata/T9.hs.expected
@@ -1,8 +0,0 @@-{-# LANGUAGE DataKinds #-} -module T9 where -import Data.Proxy (Proxy(..)) - -type P = Proxy - --- >>> Proxy :: Proxy 3 --- Proxy
+ test/testdata/TCPP.expected.hs 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/TCPP.hs view
@@ -1,19 +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 +{-# 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
@@ -1,22 +0,0 @@-{-# 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.expected.hs 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/TCompare.hs view
@@ -1,12 +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 +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
@@ -1,13 +0,0 @@-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.expected.hs view
@@ -0,0 +1,9 @@+module TEndingMulti where++-- Now trailing doctest is allowed:++{- >>> 42+>>> 54+42+54+-}
test/testdata/TEndingMulti.hs view
@@ -1,6 +1,6 @@-module TEndingMulti where - --- Now trailing doctest is allowed: - -{- >>> 42 ->>> 54-} +module TEndingMulti where++-- Now trailing doctest is allowed:++{- >>> 42+>>> 54-}
− test/testdata/TEndingMulti.hs.expected
@@ -1,9 +0,0 @@-module TEndingMulti where - --- Now trailing doctest is allowed: - -{- >>> 42 ->>> 54 -42 -54 --}
+ test/testdata/TFlags.expected.hs 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/TFlags.hs view
@@ -1,68 +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 --} +-- 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
@@ -1,76 +0,0 @@--- 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.expected.hs view
@@ -0,0 +1,42 @@+{- |+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 :: Num a => a -> a+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 :: Integer+five = 5
test/testdata/THaddock.hs view
@@ -1,35 +1,37 @@-{- | -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 +{- |+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 :: Num a => a -> a+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 :: Integer+five = 5
− test/testdata/THaddock.hs.expected
@@ -1,40 +0,0 @@-{- | -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.expected.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 view
@@ -1,9 +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" --} +-- 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
@@ -1,9 +0,0 @@--- 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.expected.hs view
@@ -0,0 +1,11 @@+module TIt where++-- >>> "test"+-- >>> it+-- "test"+-- "test"++-- >>> pure "test2"+-- >>> it+-- "test2"+-- "test2"
test/testdata/TIt.hs view
@@ -1,7 +1,7 @@-module TIt where - --- >>> "test" --- >>> it - --- >>> pure "test2" --- >>> it +module TIt where++-- >>> "test"+-- >>> it++-- >>> pure "test2"+-- >>> it
− test/testdata/TIt.hs.expected
@@ -1,11 +0,0 @@-module TIt where - --- >>> "test" --- >>> it --- "test" --- "test" - --- >>> pure "test2" --- >>> it --- "test2" --- "test2"
+ test/testdata/TLHS.expected.lhs 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/TLHS.lhs view
@@ -1,22 +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 - +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
@@ -1,25 +0,0 @@-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.expected.lhs 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/TLHSLaTeX.lhs view
@@ -1,16 +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} +\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
@@ -1,17 +0,0 @@-\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.expected.hs view
@@ -0,0 +1,9 @@+-- This works fine: {-# LANGUAGE TupleSections #-}+module TLanguageOptionsTupleSections where++-- Why oh why is this not working?+-- What is special about TupleSections?+-- >>> :set -XTupleSections+-- >>> ("a",) "b"+-- ("a","b")+
test/testdata/TLanguageOptionsTupleSections.hs view
@@ -1,9 +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") - +-- This works fine: {-# LANGUAGE TupleSections #-}+module TLanguageOptionsTupleSections where++-- Why oh why is this not working?+-- What is special about TupleSections?+-- >>> :set -XTupleSections+-- >>> ("a",) "b"+-- ("a","b")+
− test/testdata/TLanguageOptionsTupleSections.hs.expected
@@ -1,8 +0,0 @@--- 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.expected.hs view
@@ -0,0 +1,4 @@+module TLastLine where++-- >>> take 3 [1..]+-- [1,2,3]
test/testdata/TLastLine.hs view
@@ -1,3 +1,3 @@-module TLastLine where - +module TLastLine where+ -- >>> take 3 [1..]
− test/testdata/TLastLine.hs.expected
@@ -1,4 +0,0 @@-module TLastLine where - --- >>> take 3 [1..] --- [1,2,3]
+ test/testdata/TLocalImport.expected.hs view
@@ -0,0 +1,6 @@+module TLocalImport where++import qualified Util++-- >>> Util.tst 11 11+-- True
test/testdata/TLocalImport.hs view
@@ -1,5 +1,5 @@-module TLocalImport where - -import qualified Util - --- >>> Util.tst 11 11 +module TLocalImport where++import qualified Util++-- >>> Util.tst 11 11
− test/testdata/TLocalImport.hs.expected
@@ -1,6 +0,0 @@-module TLocalImport where - -import qualified Util - --- >>> Util.tst 11 11 --- True
+ test/testdata/TLocalImportInTest.expected.hs view
@@ -0,0 +1,5 @@+module TLocalImportInTest where++-- >>> import qualified Util+-- >>> Util.a+-- 'a'
test/testdata/TLocalImportInTest.hs view
@@ -1,4 +1,4 @@-module TLocalImportInTest where - --- >>> import qualified Util --- >>> Util.a +module TLocalImportInTest where++-- >>> import qualified Util+-- >>> Util.a
− test/testdata/TLocalImportInTest.hs.expected
@@ -1,5 +0,0 @@-module TLocalImportInTest where - --- >>> import qualified Util --- >>> Util.a --- 'a'
+ test/testdata/TMulti.expected.hs 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/TMulti.hs view
@@ -1,19 +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" +{- | 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
@@ -1,25 +0,0 @@-{- | 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.expected.hs view
@@ -0,0 +1,15 @@+module TNested () where+{-+>>> 54+54+{-+Nested+-}+-}++{-+{-+>>> 42+42+-}+-}
test/testdata/TNested.hs view
@@ -1,13 +1,13 @@-module TNseted () where -{- ->>> 54 -{- -Nested --} --} - -{- -{- ->>> 42 --} --} +module TNested () where+{-+>>> 54+{-+Nested+-}+-}++{-+{-+>>> 42+-}+-}
− test/testdata/TNested.hs.expected
@@ -1,15 +0,0 @@-module TNseted () where -{- ->>> 54 -54 -{- -Nested --} --} - -{- -{- ->>> 42 -42 --} --}
+ test/testdata/TNoImplicitPrelude.expected.hs 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/TNoImplicitPrelude.hs view
@@ -1,10 +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"] +{-# 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
@@ -1,11 +0,0 @@-{-# 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.expected.hs view
@@ -0,0 +1,29 @@+{- |+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 TPlainComment 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 :: Integer+five = 5
test/testdata/TPlainComment.hs view
@@ -1,25 +1,26 @@-{- | -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 +{- |+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 TPlainComment 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 :: Integer+five = 5
− test/testdata/TPlainComment.hs.expected
@@ -1,28 +0,0 @@-{- | -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.expected.hs 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/TPrelude.hs view
@@ -1,10 +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 - +-- 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
@@ -1,11 +0,0 @@--- 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.expected.hs 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/TProperty.hs view
@@ -1,5 +1,5 @@--- Support for property checking -module TProperty where - --- prop> \(l::[Bool]) -> reverse (reverse l) == l - +-- Support for property checking+module TProperty where++-- prop> \(l::[Bool]) -> reverse (reverse l) == l+
− test/testdata/TProperty.hs.expected
@@ -1,6 +0,0 @@--- Support for property checking -module TProperty where - --- prop> \(l::[Bool]) -> reverse (reverse l) == l --- +++ OK, passed 100 tests. -
+ test/testdata/TSectionEval.expected.hs 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/TSectionEval.hs view
@@ -1,15 +1,15 @@--- Tests in the same document section are executed together -module TSectionEval where - -{- ->>> 1+1 - ->>> 2+2 --} - -{- ->>> 3+3 - ->>> 4+4 --} - +-- 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
@@ -1,17 +0,0 @@--- 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.expected.hs 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/TSetup.hs view
@@ -1,10 +1,10 @@--- The setup section is executed before any other test -module TSetup where - --- $setup --- >>> x=11 --- >>> y=22 - --- >>> x+y - --- >>> x*y +-- 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
@@ -1,12 +0,0 @@--- 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.expected.hs 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/TUNPACK.hs view
@@ -1,13 +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!" +{- | 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
@@ -1,14 +0,0 @@-{- | 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
@@ -1,13 +1,13 @@--- Used for testing local imports -module Util - ( tst - , a - , b - ) -where - -tst a b = a == b - -a = 'a' - -b = 'b' +-- Used for testing local imports+module Util+ ( tst+ , a+ , b+ )+where++tst a b = a == b++a = 'a'++b = 'b'
+ test/testdata/cabal.project view
@@ -0,0 +1,1 @@+packages: ./
test/testdata/hie.yaml view
@@ -1,4 +1,4 @@-cradle: - cabal: - - path: "././" - component: "lib:test" +cradle:+ cabal:+ - path: "./"+ component: "lib:test"
test/testdata/test.cabal view
@@ -1,63 +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 - +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+