ghc-mod 5.1.0.2 → 5.8.0.0
raw patch · 250 files changed
Files
- COPYING.AGPL3 +661/−0
- COPYING.BSD3 +29/−0
- ChangeLog +130/−19
- GhcMod.hs +92/−0
- GhcMod/Exe/Boot.hs +29/−0
- GhcMod/Exe/Browse.hs +169/−0
- GhcMod/Exe/CaseSplit.hs +261/−0
- GhcMod/Exe/Check.hs +57/−0
- GhcMod/Exe/Debug.hs +182/−0
- GhcMod/Exe/FillSig.hs +590/−0
- GhcMod/Exe/Find.hs +219/−0
- GhcMod/Exe/Flag.hs +9/−0
- GhcMod/Exe/Info.hs +79/−0
- GhcMod/Exe/Internal.hs +73/−0
- GhcMod/Exe/Lang.hs +10/−0
- GhcMod/Exe/Lint.hs +30/−0
- GhcMod/Exe/Modules.hs +27/−0
- GhcMod/Exe/PkgDoc.hs +29/−0
- GhcMod/Exe/Test.hs +45/−0
- LICENSE +0/−29
- Language/Haskell/GhcMod.hs +0/−66
- Language/Haskell/GhcMod/Boot.hs +0/−25
- Language/Haskell/GhcMod/Browse.hs +0/−136
- Language/Haskell/GhcMod/Cabal16.hs +0/−45
- Language/Haskell/GhcMod/Cabal18.hs +0/−58
- Language/Haskell/GhcMod/Cabal21.hs +0/−73
- Language/Haskell/GhcMod/CabalApi.hs +0/−193
- Language/Haskell/GhcMod/CabalConfig.hs +0/−177
- Language/Haskell/GhcMod/CaseSplit.hs +0/−222
- Language/Haskell/GhcMod/Check.hs +0/−54
- Language/Haskell/GhcMod/Convert.hs +0/−135
- Language/Haskell/GhcMod/Cradle.hs +0/−109
- Language/Haskell/GhcMod/Debug.hs +0/−41
- Language/Haskell/GhcMod/Doc.hs +0/−21
- Language/Haskell/GhcMod/DynFlags.hs +0/−121
- Language/Haskell/GhcMod/Error.hs +0/−41
- Language/Haskell/GhcMod/FillSig.hs +0/−484
- Language/Haskell/GhcMod/Find.hs +0/−207
- Language/Haskell/GhcMod/Flag.hs +0/−13
- Language/Haskell/GhcMod/GHCApi.hs +0/−86
- Language/Haskell/GhcMod/GHCChoice.hs +0/−23
- Language/Haskell/GhcMod/Gap.hs +0/−475
- Language/Haskell/GhcMod/GhcPkg.hs +0/−97
- Language/Haskell/GhcMod/Info.hs +0/−65
- Language/Haskell/GhcMod/Internal.hs +0/−63
- Language/Haskell/GhcMod/Lang.hs +0/−10
- Language/Haskell/GhcMod/Lint.hs +0/−21
- Language/Haskell/GhcMod/Logger.hs +0/−232
- Language/Haskell/GhcMod/Modules.hs +0/−32
- Language/Haskell/GhcMod/Monad.hs +0/−432
- Language/Haskell/GhcMod/PkgDoc.hs +0/−26
- Language/Haskell/GhcMod/Read.hs +0/−106
- Language/Haskell/GhcMod/SrcUtils.hs +0/−106
- Language/Haskell/GhcMod/Target.hs +0/−60
- Language/Haskell/GhcMod/Types.hs +0/−122
- Language/Haskell/GhcMod/Utils.hs +0/−44
- README.md +99/−0
- Setup.hs +106/−1
- bench/Bench.hs +30/−0
- bench/data/simple-cabal/Main.hs +4/−0
- bench/data/simple-cabal/Setup.hs +2/−0
- bench/data/simple-cabal/simple-cabal.cabal +13/−0
- core/Data/Binary/Generic.hs +133/−0
- core/GhcMod/CabalHelper.hs +309/−0
- core/GhcMod/Caching.hs +141/−0
- core/GhcMod/Caching/Types.hs +67/−0
- core/GhcMod/Convert.hs +140/−0
- core/GhcMod/Cradle.hs +185/−0
- core/GhcMod/CustomPackageDb.hs +38/−0
- core/GhcMod/DebugLogger.hs +138/−0
- core/GhcMod/Doc.hs +20/−0
- core/GhcMod/DynFlags.hs +108/−0
- core/GhcMod/DynFlagsTH.hs +144/−0
- core/GhcMod/Error.hs +160/−0
- core/GhcMod/FileMapping.hs +104/−0
- core/GhcMod/Gap.hs +723/−0
- core/GhcMod/GhcPkg.hs +109/−0
- core/GhcMod/HomeModuleGraph.hs +271/−0
- core/GhcMod/LightGhc.hs +60/−0
- core/GhcMod/Logger.hs +189/−0
- core/GhcMod/Logging.hs +115/−0
- core/GhcMod/Monad.hs +148/−0
- core/GhcMod/Monad/Compat.hs_h +28/−0
- core/GhcMod/Monad/Env.hs +68/−0
- core/GhcMod/Monad/Log.hs +71/−0
- core/GhcMod/Monad/Newtypes.hs +176/−0
- core/GhcMod/Monad/Orphans.hs +83/−0
- core/GhcMod/Monad/Out.hs +52/−0
- core/GhcMod/Monad/State.hs +67/−0
- core/GhcMod/Monad/Types.hs +239/−0
- core/GhcMod/Options/DocUtils.hs +48/−0
- core/GhcMod/Options/Help.hs +79/−0
- core/GhcMod/Options/Options.hs +177/−0
- core/GhcMod/Output.hs +243/−0
- core/GhcMod/PathsAndFiles.hs +242/−0
- core/GhcMod/Pretty.hs +90/−0
- core/GhcMod/Read.hs +106/−0
- core/GhcMod/SrcUtils.hs +191/−0
- core/GhcMod/Stack.hs +97/−0
- core/GhcMod/Target.hs +515/−0
- core/GhcMod/Types.hs +406/−0
- core/GhcMod/Utils.hs +165/−0
- core/GhcMod/World.hs +55/−0
- elisp/Makefile +12/−5
- elisp/ghc-check.el +40/−31
- elisp/ghc-command.el +3/−3
- elisp/ghc-comp.el +2/−2
- elisp/ghc-doc.el +38/−18
- elisp/ghc-func.el +28/−14
- elisp/ghc-indent.el +2/−2
- elisp/ghc-info.el +4/−10
- elisp/ghc-ins-mod.el +1/−1
- elisp/ghc-pkg.el +1/−1
- elisp/ghc-process.el +137/−51
- elisp/ghc-rewrite.el +4/−7
- elisp/ghc.el +11/−8
- ghc-mod.cabal +318/−154
- shared/System/Directory/ModTime.hs +63/−0
- shared/Utils.hs +26/−0
- shelltest/ShellTest.hs +23/−0
- src/GHCMod.hs +0/−189
- src/GHCModi.hs +0/−323
- src/GhcMod/Exe/Options.hs +72/−0
- src/GhcMod/Exe/Options/Commands.hs +301/−0
- src/GhcMod/Exe/Options/ShellParse.hs +44/−0
- src/GhcMod/Exe/Version.hs +32/−0
- src/GhcModMain.hs +184/−0
- src/GhcModi.hs +55/−0
- src/Utils.hs +0/−27
- test/BrowseSpec.hs +12/−10
- test/CabalApiSpec.hs +0/−77
- test/CabalHelperSpec.hs +101/−0
- test/CaseSplitSpec.hs +81/−0
- test/CheckSpec.hs +44/−15
- test/CradleSpec.hs +67/−0
- test/CustomPackageDbSpec.hs +35/−0
- test/Dir.hs +8/−2
- test/FileMappingSpec.hs +259/−0
- test/FindSpec.hs +13/−0
- test/FlagSpec.hs +4/−3
- test/GhcPkgSpec.hs +17/−23
- test/HomeModuleGraphSpec.hs +178/−0
- test/InfoSpec.hs +36/−29
- test/LangSpec.hs +2/−1
- test/LintSpec.hs +8/−4
- test/ListSpec.hs +3/−2
- test/Main.hs +70/−9
- test/MonadSpec.hs +28/−19
- test/PathsAndFilesSpec.hs +48/−0
- test/ShellParseSpec.hs +34/−0
- test/Spec.hs +0/−1
- test/TargetSpec.hs +47/−0
- test/TestUtils.hs +87/−26
- test/data/.cabal-sandbox/i386-osx-ghc-7.6.3-packages.conf.d/Cabal-1.18.1.3-2b161c6bf77657aa17e1681d83cb051b.conf +0/−4
- test/data/.cabal-sandbox/packages/00-index.tar binary
- test/data/Bar.hs +0/−5
- test/data/Baz.hs +0/−5
- test/data/Foo.hs +0/−9
- test/data/ForeignExport.hs +0/−10
- test/data/Info.hs +0/−8
- test/data/Main.hs +0/−3
- test/data/Mutual1.hs +0/−5
- test/data/Mutual2.hs +0/−3
- test/data/Unicode.hs +0/−4
- test/data/annotations/With.hs +6/−0
- test/data/broken-cabal/cabal.sandbox.config.in +0/−25
- test/data/cabal-flags/cabal-flags.cabal +1/−2
- test/data/cabal-preprocessors/Main.hs +4/−0
- test/data/cabal-preprocessors/Preprocessed.hsc +3/−0
- test/data/cabal-preprocessors/cabal-preprocessors.cabal +14/−0
- test/data/cabal-project/.cabal-sandbox/i386-osx-ghc-7.6.3-packages.conf.d/Cabal-1.18.1.3-2b161c6bf77657aa17e1681d83cb051b.conf +4/−0
- test/data/cabal-project/Baz.hs +5/−0
- test/data/cabal-project/Foo.hs +9/−0
- test/data/cabal-project/Info.hs +8/−0
- test/data/cabal-project/Main.hs +3/−0
- test/data/cabal-project/cabalapi.cabal +67/−0
- test/data/cabal-project/subdir1/subdir2/dummy +1/−0
- test/data/cabal.sandbox.config.in +0/−25
- test/data/cabalapi.cabal +0/−67
- test/data/case-split/Crash.hs +4/−0
- test/data/case-split/Vect.hs +39/−0
- test/data/case-split/Vect706.hs +39/−0
- test/data/check-missing-warnings/DesugarWarnings.hs +5/−0
- test/data/check-packageid/cabal.sandbox.config.in +0/−25
- test/data/check-test-subdir/check-test-subdir.cabal +1/−0
- test/data/custom-cradle/custom-cradle.cabal +12/−0
- test/data/custom-cradle/ghc-mod.package-db-stack +5/−0
- test/data/custom-cradle/package-db-a/.gitkeep +0/−0
- test/data/custom-cradle/package-db-b/.gitkeep +0/−0
- test/data/custom-cradle/package-db-c/.gitkeep +0/−0
- test/data/duplicate-pkgver/cabal.sandbox.config.in +0/−25
- test/data/file-mapping/File.hs +2/−0
- test/data/file-mapping/File_Redir.hs +1/−0
- test/data/file-mapping/File_Redir_Lint.hs +4/−0
- test/data/file-mapping/lhs/File.lhs +2/−0
- test/data/file-mapping/lhs/File_Redir.lhs +1/−0
- test/data/file-mapping/lhs/File_Redir_Lint.lhs +4/−0
- test/data/file-mapping/preprocessor/File.hs +7/−0
- test/data/file-mapping/preprocessor/File_Redir.hs +6/−0
- test/data/file-mapping/preprocessor/File_Redir_Lint.hs +9/−0
- test/data/foreign-export/ForeignExport.hs +10/−0
- test/data/ghc-mod-check/Data/Foo.hs +0/−11
- test/data/ghc-mod-check/ghc-mod-check.cabal +2/−3
- test/data/ghc-mod-check/lib/Data/Foo.hs +11/−0
- test/data/hlint.hs +0/−5
- test/data/hlint/hlint.hs +5/−0
- test/data/home-module-graph/cpp/A.hs +4/−0
- test/data/home-module-graph/cpp/A1.hs +4/−0
- test/data/home-module-graph/cpp/A2.hs +1/−0
- test/data/home-module-graph/cpp/A3.hs +2/−0
- test/data/home-module-graph/cpp/B.hs +1/−0
- test/data/home-module-graph/cycle/A.hs +2/−0
- test/data/home-module-graph/cycle/B.hs +2/−0
- test/data/home-module-graph/errors/A.hs +4/−0
- test/data/home-module-graph/errors/A1.hs +4/−0
- test/data/home-module-graph/errors/A2.hs +1/−0
- test/data/home-module-graph/errors/A3.hs +2/−0
- test/data/home-module-graph/errors/B.hs +1/−0
- test/data/home-module-graph/indirect-update/A.hs +4/−0
- test/data/home-module-graph/indirect-update/A1.hs +2/−0
- test/data/home-module-graph/indirect-update/A2.hs +1/−0
- test/data/home-module-graph/indirect-update/A3.hs +2/−0
- test/data/home-module-graph/indirect-update/B.hs +1/−0
- test/data/home-module-graph/indirect-update/C.hs +1/−0
- test/data/home-module-graph/indirect/A.hs +4/−0
- test/data/home-module-graph/indirect/A1.hs +2/−0
- test/data/home-module-graph/indirect/A2.hs +2/−0
- test/data/home-module-graph/indirect/A3.hs +2/−0
- test/data/home-module-graph/indirect/B.hs +1/−0
- test/data/home-module-graph/indirect/C.hs +1/−0
- test/data/import-cycle/Mutual1.hs +5/−0
- test/data/import-cycle/Mutual2.hs +3/−0
- test/data/nice-qualification/NiceQualification.hs +4/−0
- test/data/non-exported/Fib.hs +8/−0
- test/data/pattern-synonyms/A.hs +1/−0
- test/data/pattern-synonyms/pattern-synonyms.cabal +13/−10
- test/data/quasi-quotes/FooQ.hs +6/−0
- test/data/quasi-quotes/QuasiQuotes.hs +6/−0
- test/data/stack-project/Setup.hs +2/−0
- test/data/stack-project/app/Main.hs +6/−0
- test/data/stack-project/new-template.cabal +42/−0
- test/data/stack-project/src/Lib.hs +6/−0
- test/data/stack-project/stack.yaml.in +4/−0
- test/data/stack-project/test/Spec.hs +2/−0
- test/data/subdir1/subdir2/dummy +0/−1
- test/data/target/Cpp.hs +7/−0
- test/data/template-haskell/Bar.hs +5/−0
- test/data/template-haskell/Foo.hs +9/−0
- test/data/template-haskell/ImportsTH.hs +3/−0
- test/doctests.hs +18/−9
+ COPYING.AGPL3 view
@@ -0,0 +1,661 @@+ GNU AFFERO GENERAL PUBLIC LICENSE+ Version 3, 19 November 2007++ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>+ Everyone is permitted to copy and distribute verbatim copies+ of this license document, but changing it is not allowed.++ Preamble++ The GNU Affero General Public License is a free, copyleft license for+software and other kinds of works, specifically designed to ensure+cooperation with the community in the case of network server software.++ The licenses for most software and other practical works are designed+to take away your freedom to share and change the works. By contrast,+our General Public Licenses are intended to guarantee your freedom to+share and change all versions of a program--to make sure it remains free+software for all its users.++ When we speak of free software, we are referring to freedom, not+price. Our General Public Licenses are designed to make sure that you+have the freedom to distribute copies of free software (and charge for+them if you wish), that you receive source code or can get it if you+want it, that you can change the software or use pieces of it in new+free programs, and that you know you can do these things.++ Developers that use our General Public Licenses protect your rights+with two steps: (1) assert copyright on the software, and (2) offer+you this License which gives you legal permission to copy, distribute+and/or modify the software.++ A secondary benefit of defending all users' freedom is that+improvements made in alternate versions of the program, if they+receive widespread use, become available for other developers to+incorporate. Many developers of free software are heartened and+encouraged by the resulting cooperation. However, in the case of+software used on network servers, this result may fail to come about.+The GNU General Public License permits making a modified version and+letting the public access it on a server without ever releasing its+source code to the public.++ The GNU Affero General Public License is designed specifically to+ensure that, in such cases, the modified source code becomes available+to the community. It requires the operator of a network server to+provide the source code of the modified version running there to the+users of that server. Therefore, public use of a modified version, on+a publicly accessible server, gives the public access to the source+code of the modified version.++ An older license, called the Affero General Public License and+published by Affero, was designed to accomplish similar goals. This is+a different license, not a version of the Affero GPL, but Affero has+released a new version of the Affero GPL which permits relicensing under+this license.++ The precise terms and conditions for copying, distribution and+modification follow.++ TERMS AND CONDITIONS++ 0. Definitions.++ "This License" refers to version 3 of the GNU Affero General Public License.++ "Copyright" also means copyright-like laws that apply to other kinds of+works, such as semiconductor masks.++ "The Program" refers to any copyrightable work licensed under this+License. Each licensee is addressed as "you". "Licensees" and+"recipients" may be individuals or organizations.++ To "modify" a work means to copy from or adapt all or part of the work+in a fashion requiring copyright permission, other than the making of an+exact copy. The resulting work is called a "modified version" of the+earlier work or a work "based on" the earlier work.++ A "covered work" means either the unmodified Program or a work based+on the Program.++ To "propagate" a work means to do anything with it that, without+permission, would make you directly or secondarily liable for+infringement under applicable copyright law, except executing it on a+computer or modifying a private copy. Propagation includes copying,+distribution (with or without modification), making available to the+public, and in some countries other activities as well.++ To "convey" a work means any kind of propagation that enables other+parties to make or receive copies. Mere interaction with a user through+a computer network, with no transfer of a copy, is not conveying.++ An interactive user interface displays "Appropriate Legal Notices"+to the extent that it includes a convenient and prominently visible+feature that (1) displays an appropriate copyright notice, and (2)+tells the user that there is no warranty for the work (except to the+extent that warranties are provided), that licensees may convey the+work under this License, and how to view a copy of this License. If+the interface presents a list of user commands or options, such as a+menu, a prominent item in the list meets this criterion.++ 1. Source Code.++ The "source code" for a work means the preferred form of the work+for making modifications to it. "Object code" means any non-source+form of a work.++ A "Standard Interface" means an interface that either is an official+standard defined by a recognized standards body, or, in the case of+interfaces specified for a particular programming language, one that+is widely used among developers working in that language.++ The "System Libraries" of an executable work include anything, other+than the work as a whole, that (a) is included in the normal form of+packaging a Major Component, but which is not part of that Major+Component, and (b) serves only to enable use of the work with that+Major Component, or to implement a Standard Interface for which an+implementation is available to the public in source code form. A+"Major Component", in this context, means a major essential component+(kernel, window system, and so on) of the specific operating system+(if any) on which the executable work runs, or a compiler used to+produce the work, or an object code interpreter used to run it.++ The "Corresponding Source" for a work in object code form means all+the source code needed to generate, install, and (for an executable+work) run the object code and to modify the work, including scripts to+control those activities. However, it does not include the work's+System Libraries, or general-purpose tools or generally available free+programs which are used unmodified in performing those activities but+which are not part of the work. For example, Corresponding Source+includes interface definition files associated with source files for+the work, and the source code for shared libraries and dynamically+linked subprograms that the work is specifically designed to require,+such as by intimate data communication or control flow between those+subprograms and other parts of the work.++ The Corresponding Source need not include anything that users+can regenerate automatically from other parts of the Corresponding+Source.++ The Corresponding Source for a work in source code form is that+same work.++ 2. Basic Permissions.++ All rights granted under this License are granted for the term of+copyright on the Program, and are irrevocable provided the stated+conditions are met. This License explicitly affirms your unlimited+permission to run the unmodified Program. The output from running a+covered work is covered by this License only if the output, given its+content, constitutes a covered work. This License acknowledges your+rights of fair use or other equivalent, as provided by copyright law.++ You may make, run and propagate covered works that you do not+convey, without conditions so long as your license otherwise remains+in force. You may convey covered works to others for the sole purpose+of having them make modifications exclusively for you, or provide you+with facilities for running those works, provided that you comply with+the terms of this License in conveying all material for which you do+not control copyright. Those thus making or running the covered works+for you must do so exclusively on your behalf, under your direction+and control, on terms that prohibit them from making any copies of+your copyrighted material outside their relationship with you.++ Conveying under any other circumstances is permitted solely under+the conditions stated below. Sublicensing is not allowed; section 10+makes it unnecessary.++ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.++ No covered work shall be deemed part of an effective technological+measure under any applicable law fulfilling obligations under article+11 of the WIPO copyright treaty adopted on 20 December 1996, or+similar laws prohibiting or restricting circumvention of such+measures.++ When you convey a covered work, you waive any legal power to forbid+circumvention of technological measures to the extent such circumvention+is effected by exercising rights under this License with respect to+the covered work, and you disclaim any intention to limit operation or+modification of the work as a means of enforcing, against the work's+users, your or third parties' legal rights to forbid circumvention of+technological measures.++ 4. Conveying Verbatim Copies.++ You may convey verbatim copies of the Program's source code as you+receive it, in any medium, provided that you conspicuously and+appropriately publish on each copy an appropriate copyright notice;+keep intact all notices stating that this License and any+non-permissive terms added in accord with section 7 apply to the code;+keep intact all notices of the absence of any warranty; and give all+recipients a copy of this License along with the Program.++ You may charge any price or no price for each copy that you convey,+and you may offer support or warranty protection for a fee.++ 5. Conveying Modified Source Versions.++ You may convey a work based on the Program, or the modifications to+produce it from the Program, in the form of source code under the+terms of section 4, provided that you also meet all of these conditions:++ a) The work must carry prominent notices stating that you modified+ it, and giving a relevant date.++ b) The work must carry prominent notices stating that it is+ released under this License and any conditions added under section+ 7. This requirement modifies the requirement in section 4 to+ "keep intact all notices".++ c) You must license the entire work, as a whole, under this+ License to anyone who comes into possession of a copy. This+ License will therefore apply, along with any applicable section 7+ additional terms, to the whole of the work, and all its parts,+ regardless of how they are packaged. This License gives no+ permission to license the work in any other way, but it does not+ invalidate such permission if you have separately received it.++ d) If the work has interactive user interfaces, each must display+ Appropriate Legal Notices; however, if the Program has interactive+ interfaces that do not display Appropriate Legal Notices, your+ work need not make them do so.++ A compilation of a covered work with other separate and independent+works, which are not by their nature extensions of the covered work,+and which are not combined with it such as to form a larger program,+in or on a volume of a storage or distribution medium, is called an+"aggregate" if the compilation and its resulting copyright are not+used to limit the access or legal rights of the compilation's users+beyond what the individual works permit. Inclusion of a covered work+in an aggregate does not cause this License to apply to the other+parts of the aggregate.++ 6. Conveying Non-Source Forms.++ You may convey a covered work in object code form under the terms+of sections 4 and 5, provided that you also convey the+machine-readable Corresponding Source under the terms of this License,+in one of these ways:++ a) Convey the object code in, or embodied in, a physical product+ (including a physical distribution medium), accompanied by the+ Corresponding Source fixed on a durable physical medium+ customarily used for software interchange.++ b) Convey the object code in, or embodied in, a physical product+ (including a physical distribution medium), accompanied by a+ written offer, valid for at least three years and valid for as+ long as you offer spare parts or customer support for that product+ model, to give anyone who possesses the object code either (1) a+ copy of the Corresponding Source for all the software in the+ product that is covered by this License, on a durable physical+ medium customarily used for software interchange, for a price no+ more than your reasonable cost of physically performing this+ conveying of source, or (2) access to copy the+ Corresponding Source from a network server at no charge.++ c) Convey individual copies of the object code with a copy of the+ written offer to provide the Corresponding Source. This+ alternative is allowed only occasionally and noncommercially, and+ only if you received the object code with such an offer, in accord+ with subsection 6b.++ d) Convey the object code by offering access from a designated+ place (gratis or for a charge), and offer equivalent access to the+ Corresponding Source in the same way through the same place at no+ further charge. You need not require recipients to copy the+ Corresponding Source along with the object code. If the place to+ copy the object code is a network server, the Corresponding Source+ may be on a different server (operated by you or a third party)+ that supports equivalent copying facilities, provided you maintain+ clear directions next to the object code saying where to find the+ Corresponding Source. Regardless of what server hosts the+ Corresponding Source, you remain obligated to ensure that it is+ available for as long as needed to satisfy these requirements.++ e) Convey the object code using peer-to-peer transmission, provided+ you inform other peers where the object code and Corresponding+ Source of the work are being offered to the general public at no+ charge under subsection 6d.++ A separable portion of the object code, whose source code is excluded+from the Corresponding Source as a System Library, need not be+included in conveying the object code work.++ A "User Product" is either (1) a "consumer product", which means any+tangible personal property which is normally used for personal, family,+or household purposes, or (2) anything designed or sold for incorporation+into a dwelling. In determining whether a product is a consumer product,+doubtful cases shall be resolved in favor of coverage. For a particular+product received by a particular user, "normally used" refers to a+typical or common use of that class of product, regardless of the status+of the particular user or of the way in which the particular user+actually uses, or expects or is expected to use, the product. A product+is a consumer product regardless of whether the product has substantial+commercial, industrial or non-consumer uses, unless such uses represent+the only significant mode of use of the product.++ "Installation Information" for a User Product means any methods,+procedures, authorization keys, or other information required to install+and execute modified versions of a covered work in that User Product from+a modified version of its Corresponding Source. The information must+suffice to ensure that the continued functioning of the modified object+code is in no case prevented or interfered with solely because+modification has been made.++ If you convey an object code work under this section in, or with, or+specifically for use in, a User Product, and the conveying occurs as+part of a transaction in which the right of possession and use of the+User Product is transferred to the recipient in perpetuity or for a+fixed term (regardless of how the transaction is characterized), the+Corresponding Source conveyed under this section must be accompanied+by the Installation Information. But this requirement does not apply+if neither you nor any third party retains the ability to install+modified object code on the User Product (for example, the work has+been installed in ROM).++ The requirement to provide Installation Information does not include a+requirement to continue to provide support service, warranty, or updates+for a work that has been modified or installed by the recipient, or for+the User Product in which it has been modified or installed. Access to a+network may be denied when the modification itself materially and+adversely affects the operation of the network or violates the rules and+protocols for communication across the network.++ Corresponding Source conveyed, and Installation Information provided,+in accord with this section must be in a format that is publicly+documented (and with an implementation available to the public in+source code form), and must require no special password or key for+unpacking, reading or copying.++ 7. Additional Terms.++ "Additional permissions" are terms that supplement the terms of this+License by making exceptions from one or more of its conditions.+Additional permissions that are applicable to the entire Program shall+be treated as though they were included in this License, to the extent+that they are valid under applicable law. If additional permissions+apply only to part of the Program, that part may be used separately+under those permissions, but the entire Program remains governed by+this License without regard to the additional permissions.++ When you convey a copy of a covered work, you may at your option+remove any additional permissions from that copy, or from any part of+it. (Additional permissions may be written to require their own+removal in certain cases when you modify the work.) You may place+additional permissions on material, added by you to a covered work,+for which you have or can give appropriate copyright permission.++ Notwithstanding any other provision of this License, for material you+add to a covered work, you may (if authorized by the copyright holders of+that material) supplement the terms of this License with terms:++ a) Disclaiming warranty or limiting liability differently from the+ terms of sections 15 and 16 of this License; or++ b) Requiring preservation of specified reasonable legal notices or+ author attributions in that material or in the Appropriate Legal+ Notices displayed by works containing it; or++ c) Prohibiting misrepresentation of the origin of that material, or+ requiring that modified versions of such material be marked in+ reasonable ways as different from the original version; or++ d) Limiting the use for publicity purposes of names of licensors or+ authors of the material; or++ e) Declining to grant rights under trademark law for use of some+ trade names, trademarks, or service marks; or++ f) Requiring indemnification of licensors and authors of that+ material by anyone who conveys the material (or modified versions of+ it) with contractual assumptions of liability to the recipient, for+ any liability that these contractual assumptions directly impose on+ those licensors and authors.++ All other non-permissive additional terms are considered "further+restrictions" within the meaning of section 10. If the Program as you+received it, or any part of it, contains a notice stating that it is+governed by this License along with a term that is a further+restriction, you may remove that term. If a license document contains+a further restriction but permits relicensing or conveying under this+License, you may add to a covered work material governed by the terms+of that license document, provided that the further restriction does+not survive such relicensing or conveying.++ If you add terms to a covered work in accord with this section, you+must place, in the relevant source files, a statement of the+additional terms that apply to those files, or a notice indicating+where to find the applicable terms.++ Additional terms, permissive or non-permissive, may be stated in the+form of a separately written license, or stated as exceptions;+the above requirements apply either way.++ 8. Termination.++ You may not propagate or modify a covered work except as expressly+provided under this License. Any attempt otherwise to propagate or+modify it is void, and will automatically terminate your rights under+this License (including any patent licenses granted under the third+paragraph of section 11).++ However, if you cease all violation of this License, then your+license from a particular copyright holder is reinstated (a)+provisionally, unless and until the copyright holder explicitly and+finally terminates your license, and (b) permanently, if the copyright+holder fails to notify you of the violation by some reasonable means+prior to 60 days after the cessation.++ Moreover, your license from a particular copyright holder is+reinstated permanently if the copyright holder notifies you of the+violation by some reasonable means, this is the first time you have+received notice of violation of this License (for any work) from that+copyright holder, and you cure the violation prior to 30 days after+your receipt of the notice.++ Termination of your rights under this section does not terminate the+licenses of parties who have received copies or rights from you under+this License. If your rights have been terminated and not permanently+reinstated, you do not qualify to receive new licenses for the same+material under section 10.++ 9. Acceptance Not Required for Having Copies.++ You are not required to accept this License in order to receive or+run a copy of the Program. Ancillary propagation of a covered work+occurring solely as a consequence of using peer-to-peer transmission+to receive a copy likewise does not require acceptance. However,+nothing other than this License grants you permission to propagate or+modify any covered work. These actions infringe copyright if you do+not accept this License. Therefore, by modifying or propagating a+covered work, you indicate your acceptance of this License to do so.++ 10. Automatic Licensing of Downstream Recipients.++ Each time you convey a covered work, the recipient automatically+receives a license from the original licensors, to run, modify and+propagate that work, subject to this License. You are not responsible+for enforcing compliance by third parties with this License.++ An "entity transaction" is a transaction transferring control of an+organization, or substantially all assets of one, or subdividing an+organization, or merging organizations. If propagation of a covered+work results from an entity transaction, each party to that+transaction who receives a copy of the work also receives whatever+licenses to the work the party's predecessor in interest had or could+give under the previous paragraph, plus a right to possession of the+Corresponding Source of the work from the predecessor in interest, if+the predecessor has it or can get it with reasonable efforts.++ You may not impose any further restrictions on the exercise of the+rights granted or affirmed under this License. For example, you may+not impose a license fee, royalty, or other charge for exercise of+rights granted under this License, and you may not initiate litigation+(including a cross-claim or counterclaim in a lawsuit) alleging that+any patent claim is infringed by making, using, selling, offering for+sale, or importing the Program or any portion of it.++ 11. Patents.++ A "contributor" is a copyright holder who authorizes use under this+License of the Program or a work on which the Program is based. The+work thus licensed is called the contributor's "contributor version".++ A contributor's "essential patent claims" are all patent claims+owned or controlled by the contributor, whether already acquired or+hereafter acquired, that would be infringed by some manner, permitted+by this License, of making, using, or selling its contributor version,+but do not include claims that would be infringed only as a+consequence of further modification of the contributor version. For+purposes of this definition, "control" includes the right to grant+patent sublicenses in a manner consistent with the requirements of+this License.++ Each contributor grants you a non-exclusive, worldwide, royalty-free+patent license under the contributor's essential patent claims, to+make, use, sell, offer for sale, import and otherwise run, modify and+propagate the contents of its contributor version.++ In the following three paragraphs, a "patent license" is any express+agreement or commitment, however denominated, not to enforce a patent+(such as an express permission to practice a patent or covenant not to+sue for patent infringement). To "grant" such a patent license to a+party means to make such an agreement or commitment not to enforce a+patent against the party.++ If you convey a covered work, knowingly relying on a patent license,+and the Corresponding Source of the work is not available for anyone+to copy, free of charge and under the terms of this License, through a+publicly available network server or other readily accessible means,+then you must either (1) cause the Corresponding Source to be so+available, or (2) arrange to deprive yourself of the benefit of the+patent license for this particular work, or (3) arrange, in a manner+consistent with the requirements of this License, to extend the patent+license to downstream recipients. "Knowingly relying" means you have+actual knowledge that, but for the patent license, your conveying the+covered work in a country, or your recipient's use of the covered work+in a country, would infringe one or more identifiable patents in that+country that you have reason to believe are valid.++ If, pursuant to or in connection with a single transaction or+arrangement, you convey, or propagate by procuring conveyance of, a+covered work, and grant a patent license to some of the parties+receiving the covered work authorizing them to use, propagate, modify+or convey a specific copy of the covered work, then the patent license+you grant is automatically extended to all recipients of the covered+work and works based on it.++ A patent license is "discriminatory" if it does not include within+the scope of its coverage, prohibits the exercise of, or is+conditioned on the non-exercise of one or more of the rights that are+specifically granted under this License. You may not convey a covered+work if you are a party to an arrangement with a third party that is+in the business of distributing software, under which you make payment+to the third party based on the extent of your activity of conveying+the work, and under which the third party grants, to any of the+parties who would receive the covered work from you, a discriminatory+patent license (a) in connection with copies of the covered work+conveyed by you (or copies made from those copies), or (b) primarily+for and in connection with specific products or compilations that+contain the covered work, unless you entered into that arrangement,+or that patent license was granted, prior to 28 March 2007.++ Nothing in this License shall be construed as excluding or limiting+any implied license or other defenses to infringement that may+otherwise be available to you under applicable patent law.++ 12. No Surrender of Others' Freedom.++ If conditions are imposed on you (whether by court order, agreement or+otherwise) that contradict the conditions of this License, they do not+excuse you from the conditions of this License. If you cannot convey a+covered work so as to satisfy simultaneously your obligations under this+License and any other pertinent obligations, then as a consequence you may+not convey it at all. For example, if you agree to terms that obligate you+to collect a royalty for further conveying from those to whom you convey+the Program, the only way you could satisfy both those terms and this+License would be to refrain entirely from conveying the Program.++ 13. Remote Network Interaction; Use with the GNU General Public License.++ Notwithstanding any other provision of this License, if you modify the+Program, your modified version must prominently offer all users+interacting with it remotely through a computer network (if your version+supports such interaction) an opportunity to receive the Corresponding+Source of your version by providing access to the Corresponding Source+from a network server at no charge, through some standard or customary+means of facilitating copying of software. This Corresponding Source+shall include the Corresponding Source for any work covered by version 3+of the GNU General Public License that is incorporated pursuant to the+following paragraph.++ Notwithstanding any other provision of this License, you have+permission to link or combine any covered work with a work licensed+under version 3 of the GNU General Public License into a single+combined work, and to convey the resulting work. The terms of this+License will continue to apply to the part which is the covered work,+but the work with which it is combined will remain governed by version+3 of the GNU General Public License.++ 14. Revised Versions of this License.++ The Free Software Foundation may publish revised and/or new versions of+the GNU Affero General Public License from time to time. Such new versions+will be similar in spirit to the present version, but may differ in detail to+address new problems or concerns.++ Each version is given a distinguishing version number. If the+Program specifies that a certain numbered version of the GNU Affero General+Public License "or any later version" applies to it, you have the+option of following the terms and conditions either of that numbered+version or of any later version published by the Free Software+Foundation. If the Program does not specify a version number of the+GNU Affero General Public License, you may choose any version ever published+by the Free Software Foundation.++ If the Program specifies that a proxy can decide which future+versions of the GNU Affero General Public License can be used, that proxy's+public statement of acceptance of a version permanently authorizes you+to choose that version for the Program.++ Later license versions may give you additional or different+permissions. However, no additional obligations are imposed on any+author or copyright holder as a result of your choosing to follow a+later version.++ 15. Disclaimer of Warranty.++ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.++ 16. Limitation of Liability.++ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF+SUCH DAMAGES.++ 17. Interpretation of Sections 15 and 16.++ If the disclaimer of warranty and limitation of liability provided+above cannot be given local legal effect according to their terms,+reviewing courts shall apply local law that most closely approximates+an absolute waiver of all civil liability in connection with the+Program, unless a warranty or assumption of liability accompanies a+copy of the Program in return for a fee.++ END OF TERMS AND CONDITIONS++ How to Apply These Terms to Your New Programs++ If you develop a new program, and you want it to be of the greatest+possible use to the public, the best way to achieve this is to make it+free software which everyone can redistribute and change under these terms.++ To do so, attach the following notices to the program. It is safest+to attach them to the start of each source file to most effectively+state the exclusion of warranty; and each file should have at least+the "copyright" line and a pointer to where the full notice is found.++ <one line to give the program's name and a brief idea of what it does.>+ Copyright (C) <year> <name of author>++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU Affero General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU Affero General Public License for more details.++ You should have received a copy of the GNU Affero General Public License+ along with this program. If not, see <http://www.gnu.org/licenses/>.++Also add information on how to contact you by electronic and paper mail.++ If your software can interact with users remotely through a computer+network, you should also make sure that it provides a way for users to+get its source. For example, if your program is a web application, its+interface could display a "Source" link that leads users to an archive+of the code. There are many ways you could offer source, and different+solutions will be better for different programs; see section 13 for the+specific requirements.++ You should also get your employer (if you work as a programmer) or school,+if any, to sign a "copyright disclaimer" for the program, if necessary.+For more information on this, and how to apply and follow the GNU AGPL, see+<http://www.gnu.org/licenses/>.
+ COPYING.BSD3 view
@@ -0,0 +1,29 @@+Copyright (c) 2009, IIJ Innovation Institute Inc.+All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions+are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.+ * Redistributions in binary form must reproduce the above copyright+ notice, this list of conditions and the following disclaimer in+ the documentation and/or other materials provided with the+ distribution.+ * Neither the name of the copyright holders nor the names of its+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE+COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN+ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE+POSSIBILITY OF SUCH DAMAGE.
ChangeLog view
@@ -1,33 +1,144 @@+2017-05-26 v5.8.0.0+ * Fix logic bug in fix for excessive use of `map-file`+ * Bump HLint to 2.x+ * Reorganize Cabal file to make maintanance easier+ * Merge #872, Do not log warning when Stack project is preferred+ * Merge #873, Fix build on case-insensitive filesystems+ * Fix 'debug' command when ghc(-pkg) not on PATH+ * Rework README+ * Reorganize modules as preparation for splitting off ghc-mod-core+ * Remove dependency on 'pretty' and use GHC's pretty printer instead+ * Merge #854, Fix for "ghc-mod doc" when usind with stack+ * Merge #858, Fix Gap.fromTyThing returning GHC internal+ representation instead of the user readable representation of+ types+ * Fix #774, 'find*File' searching all the way up to /+ * Fix #778, Check directory permissions before reading in+ findFileInParentsP+ * Merge #817, fix #779, bad "ghc-mod check" performance++2017-01-16 v5.7.0.0+ * Bump cabal-helper to 0.7.3.0 to support Cabal-1.24.1.0+ * Bump haskell-src-exts, optparse-applicative, pipes and extra+ to be compatible with stackage.++2016-07-29 v5.6.0.0+ * Bump cabal-helper to 0.7, adds support for Cabal-1.24+ * Merge #737, `map-file` caching issues+ * Merge #767, Add `browse` option to show symbol "parents"+ * Merge #731, Type constraints+ * Fix #69 (via #731), Missing type constraints+ * Fix #438, Case splitting not working+ * Fix #790, Don't try to use 'cabal' or 'stack' when it's not installed+ * Add support for GHC 8.0++2016-01-19 v5.5.0.0+ * Fix #660, cabal-helper errors when no global GHC is installed (Stack)+ * Fix #665, Reinstate internally managed CWD (no more `ghc-mod root`+ requirement for frontends)+ * Merge #707, Support for spaces in file names when using+ legacy-interactive+ * Merge #694, #706, #703, Rewrite command line parser using+ optparse-applicative. Thanks @lierdakil!+ * Merge #693, Fix slowdown and bugs caused by excessive use of+ `map-file`+ * Fix #678, "No instance nor default method for class operation put"+ * Fix #683, #684, a variety of caching related issues+ * Fix #666, The issue of the beast >:3+ * Merge #649, elisp: Add ghc-report-errors to inhibit *GHC Error*+ logging+ * Fix #621, Preserve Cabal flag selection across automatic+ reconfiguration++2015-09-16 v5.4.0.0+ * Add support for the Stack build tool+ * Fix #554, `module not interpreted` errors when using the `type`+ command+ * Merge #484, support for file redirection+ * Add support for file redirection to Emacs frontend so+ all commands should work even with unsaved files now!+ * Support inserting holes in type signatures+ * Merge #543, Fix URL anchors being dropped in OS X+ * Fix GHC session always being dropped in interactive mode (caused+ super slowness)+ * Expose all internal modules because API will get a major+ redesign soon anyways+ * ghc-mod(i) executable must now be run in project directory for+ commands other than `root`+ * Add --line-prefix option for multiplexing stdout/err onto one stream++2015-08-14 v5.3.0.0+ * Re-license majority of code under the AGPL-3+ * Add support for GHC 7.10 and Cabal 1.22+ * Remove `cabalDependPackages', `cabalAllTargets'+ * Merge #434, Fix finding sandbox config file and directory.+ * Merge #431, Re-add output line separator global option for expand+ command.+ * Merge #470, Support for overriding the package-db stack+ * Merge #486, Fix ineffective cache invalidation for `find`++2014-12-31 v5.2.1.2+ * Merge #377, Fix `browse` erroneously thinking haskell2010 identifiers+ are operators+ * Fix incompatibility with monad-control >= 1.0.0+ * Fix temporary directories not being removed properly+ * Merge #405, #408, a race condition in the Emacs frontend+ * Merge #403, Support unicode quotes in module regexp++2014-11-03 v5.2.1.1+ * Fix `findCabalFiles` thinking `$HOME/.cabal` is a cabal file.+ * Support `where` clauses, `let` bindings and `case` expressions+ in case splitting, #400++2014-11-02 v5.2.1.0+ * Fix `newTempDir` on Windows+ * GhcModT's liftIO instance now converts GhcMOdError exceptions+ into monadic failures++2014-10-30 v5.2.0.0+ * Return type of `loadSymbolDb` is now in GhcModT+ * Function `dumpSymbol` now takes the path of the target directory+ * Fix #387, Pattern match failure in GhcPkg+ * Fix #386, `ghc-mod version` should not check `cabal configure`+ * Fix #391, Error on command `-g` when used before command despite+ --help output saying this is valid+ * Fix formatting of `ghc-version` constant in the elisp code. in+ version 5.1.1.0 the string was "v5.1.1.0" instead of "5.1.1.0".++2014-10-04 v5.1.1.0+ * Handle various consistency related issues: #222, #224, #326, #332+ * Add `isOutdated` to Language.Haskell.GhcMod+ 2014-09-17 v5.1.0.2- * Fix building with haskell-src-exts < 1.16.0+ * Fix building with haskell-src-exts < 1.16.0 2014-09-16 v5.1.0.1- * Fix building with haskell-src-exts-1.16.0- * Loosen monad-journal dependency+ * Fix building with haskell-src-exts-1.16.0+ * Loosen monad-journal dependency 2014-09-12 v5.1.0.0- * GhcModError is now a recursive data type (`GMECabalConfigure`'s- type changed)- * GhcModT's MonadIO instance now converts IOError's to failures in- the ErrorT part of GhcModT on `liftIO`.- * Make `loadSymbolDb` polimorphic in the return types's monad.- * Add `hoistGhcModT` to Language.Haskell.GhcMod.Internal- * Fix `check` command for modules using `-XPatternSynonyms`- * Merge #364, Support cabal configuration flags+ * GhcModError is now a recursive data type (`GMECabalConfigure`'s+ type changed)+ * GhcModT's MonadIO instance now converts IOError's to failures in+ the ErrorT part of GhcModT on `liftIO`.+ * Make `loadSymbolDb` polimorphic in the return types's monad.+ * Add `hoistGhcModT` to Language.Haskell.GhcMod.Internal+ * Fix `check` command for modules using `-XPatternSynonyms`+ * Merge #364, Support cabal configuration flags 2014-08-29 v5.0.1.2- * Merge #345, Try fixing duplicate errors- * Merge #344, elisp: Use advice to check syntax on save-buffer- * Merge #341, support `browse -d` in ghc-modi- * Merge #352, elisp: Fix C-u accidentally getting turned into a- prefix command+ * Merge #345, Try fixing duplicate errors+ * Merge #344, elisp: Use advice to check syntax on save-buffer+ * Merge #341, support `browse -d` in ghc-modi+ * Merge #352, elisp: Fix C-u accidentally getting turned into a+ prefix command 2014-08-24 v5.0.1.1- * Fix CaseSplitting faliure when using "fancy types" (see #336)- * Print error information in "spec" test suite when using `extract`+ * Fix CaseSplitting faliure when using "fancy types" (see #336)+ * Print error information in "spec" test suite when using `extract` 2014-08-20 v5.0.1- * Fix missing file in "Data-Files"+ * Fix missing file in "Data-Files" 2014-08-20 v5.0.0 * ghc-mod consumes much less memory than ghc-mod-4.1.
+ GhcMod.hs view
@@ -0,0 +1,92 @@+-- | The ghc-mod library.++module GhcMod (+ -- * Cradle+ Cradle(..)+ , Project(..)+ , findCradle+ -- * Options+ , Options(..)+ , LineSeparator(..)+ , OutputStyle(..)+ , FileMapping(..)+ , defaultOptions+ -- * Logging+ , GmLogLevel+ , increaseLogLevel+ , decreaseLogLevel+ , gmSetLogLevel+ , gmLog+ -- * Types+ , ModuleString+ , Expression(..)+ , GhcPkgDb+ , Symbol+ , SymbolDb+ , GhcModError(..)+ -- * Monad Types+ , GhcModT+ , IOish+ -- * Monad utilities+ , runGhcModT+ , withOptions+ , dropSession+ -- * 'GhcMod' utilities+ , boot+ , browse+ , check+ , checkSyntax+ , debugInfo+ , componentInfo+ , expandTemplate+ , info+ , lint+ , pkgDoc+ , rootInfo+ , types+ , test+ , splits+ , sig+ , refine+ , auto+ , modules+ , languages+ , flags+ , findSymbol+ , lookupSymbol+ , dumpSymbol+ -- * SymbolDb+ , loadSymbolDb+ , isOutdated+ -- * Output+ , gmPutStr+ , gmErrStr+ , gmPutStrLn+ , gmErrStrLn+ -- * FileMapping+ , loadMappedFile+ , loadMappedFileSource+ , unloadMappedFile+ ) where++import GhcMod.Exe.Boot+import GhcMod.Exe.Browse+import GhcMod.Exe.CaseSplit+import GhcMod.Exe.Check+import GhcMod.Exe.Debug+import GhcMod.Exe.FillSig+import GhcMod.Exe.Find+import GhcMod.Exe.Flag+import GhcMod.Exe.Info+import GhcMod.Exe.Lang+import GhcMod.Exe.Lint+import GhcMod.Exe.Modules+import GhcMod.Exe.PkgDoc+import GhcMod.Exe.Test+import GhcMod.Cradle+import GhcMod.FileMapping+import GhcMod.Logging+import GhcMod.Monad+import GhcMod.Output+import GhcMod.Target+import GhcMod.Types
+ GhcMod/Exe/Boot.hs view
@@ -0,0 +1,29 @@+module GhcMod.Exe.Boot where++import Control.Applicative+import Prelude++import GhcMod.Exe.Browse+import GhcMod.Exe.Flag+import GhcMod.Exe.Lang+import GhcMod.Exe.Modules+import GhcMod.Monad+import GhcMod.Types (defaultBrowseOpts)++-- | Printing necessary information for front-end booting.+boot :: IOish m => GhcModT m String+boot = concat <$> sequence ms+ where+ ms = [modules False, languages, flags, concat <$> mapM (browse defaultBrowseOpts) preBrowsedModules]++preBrowsedModules :: [String]+preBrowsedModules = [+ "Prelude"+ , "Control.Applicative"+ , "Control.Exception"+ , "Control.Monad"+ , "Data.Char"+ , "Data.List"+ , "Data.Maybe"+ , "System.IO"+ ]
+ GhcMod/Exe/Browse.hs view
@@ -0,0 +1,169 @@+{-# LANGUAGE CPP #-}+module GhcMod.Exe.Browse (+ browse,+ BrowseOpts(..)+ ) where++import Safe+import Control.Applicative+import Control.Exception (SomeException(..))+import Data.Char+import Data.List+import Data.Maybe+import FastString+import GHC+import HscTypes+import qualified GHC as G+import GhcMod.Convert+import GhcMod.Doc (showPage, styleUnqualified)+import GhcMod.Gap as Gap+import GhcMod.Types+import GhcMod.Monad+import GhcMod.Logging+import Name (getOccString)+import Outputable+import TyCon (isAlgTyCon)+import Type (dropForAlls, splitFunTy_maybe, mkFunTy, isPredTy)+import Exception (ExceptionMonad, ghandle)+import Prelude+#if __GLASGOW_HASKELL__ >= 800+import PatSyn (pprPatSynType)+#endif++----------------------------------------------------------------++-- | Getting functions, classes, etc from a module.+browse :: forall m. IOish m+ => BrowseOpts -- ^ Configuration parameters+ -> String -- ^ A module name. (e.g. \"Data.List\", "base:Prelude")+ -> GhcModT m String+browse opts pkgmdl = do+ convert' . sort =<< go+ where+ -- TODO: Add API to Gm.Target to check if module is home module without+ -- bringing up a GHC session as well then this can be made a lot cleaner+ go = ghandle (\ex@(SomeException _) -> logException ex >> return []) $ do+ goPkgModule `G.gcatch` (\(SomeException _) -> goHomeModule)++ logException ex =+ gmLog GmException "browse" $ showToDoc ex++ goPkgModule = do+ runGmPkgGhc $+ processExports opts =<< tryModuleInfo =<< G.findModule mdlname mpkgid++ goHomeModule = runGmlT [Right mdlname] $ do+ processExports opts =<< tryModuleInfo =<< G.findModule mdlname Nothing++ tryModuleInfo m = fromJustNote "browse, tryModuleInfo" <$> G.getModuleInfo m++ (mpkg, mdl) = splitPkgMdl pkgmdl+ mdlname = G.mkModuleName mdl+ mpkgid = mkFastString <$> mpkg++-- |+--+-- >>> splitPkgMdl "base:Prelude"+-- (Just "base","Prelude")+-- >>> splitPkgMdl "Prelude"+-- (Nothing,"Prelude")+splitPkgMdl :: String -> (Maybe String,String)+splitPkgMdl pkgmdl =+ case break (==':') pkgmdl of+ (mdl, "") -> (Nothing, mdl)+ (pkg, _:mdl) -> (Just pkg, mdl)++-- Haskell 2010:+-- small -> ascSmall | uniSmall | _+-- ascSmall -> a | b | ... | z+-- uniSmall -> any Unicode lowercase letter+-- varid -> (small {small | large | digit | ' })++isNotOp :: String -> Bool+isNotOp (h:_) = isAlpha h || (h == '_')+isNotOp _ = error "isNotOp"++processExports :: (G.GhcMonad m, MonadIO m, ExceptionMonad m)+ => BrowseOpts -> ModuleInfo -> m [String]+processExports opt minfo = do+ let+ removeOps+ | optBrowseOperators opt = id+ | otherwise = filter (isNotOp . getOccString)+ mapM (showExport opt minfo) $ removeOps $ G.modInfoExports minfo++showExport :: forall m. (G.GhcMonad m, MonadIO m, ExceptionMonad m)+ => BrowseOpts -> ModuleInfo -> Name -> m String+showExport opt minfo e = do+ mtype' <- mtype+ return $ concat $ catMaybes [mqualified, Just $ formatOp $ getOccString e, mtype']+ where+ mqualified = (G.moduleNameString (G.moduleName $ G.nameModule e) ++ ".") `justIf` optBrowseQualified opt+ mtype :: m (Maybe String)+ mtype+ | optBrowseDetailed opt || optBrowseParents opt = do+ tyInfo <- G.modInfoLookupName minfo e+ -- If nothing found, load dependent module and lookup global+ tyResult <- maybe (inOtherModule e) (return . Just) tyInfo+ dflag <- G.getSessionDynFlags+ let sig = do+ typeName <- tyResult >>= showThing dflag+ (" :: " ++ typeName) `justIf` optBrowseDetailed opt+ let parent = do+ thing <- fmap getOccString $ tyResult >>= tyThingParent_maybe+ (" -- from:" ++ thing) `justIf` optBrowseParents opt+ return $ case concat $ catMaybes [sig, parent] of+ [] -> Nothing+ x -> Just x+ | otherwise = return Nothing+ formatOp nm+ | null nm = error "formatOp"+ | isNotOp nm = nm+ | otherwise = "(" ++ nm ++ ")"+ inOtherModule :: Name -> m (Maybe TyThing)+ inOtherModule nm = do+ G.getModuleInfo (G.nameModule nm) >> G.lookupGlobalName nm+ justIf :: a -> Bool -> Maybe a+ justIf x True = Just x+ justIf _ False = Nothing++showThing :: DynFlags -> TyThing -> Maybe String+showThing dflag tything = showThing' dflag (fromTyThing tything)++showThing' :: DynFlags -> GapThing -> Maybe String+showThing' dflag (GtA a) = Just $ formatType dflag a+showThing' _ (GtT t) = unwords . toList <$> tyType t+ where+ toList t' = t' : getOccString t : map getOccString (G.tyConTyVars t)+#if __GLASGOW_HASKELL__ >= 800+showThing' dflag (GtPatSyn p) = Just $ showSDoc dflag $ pprPatSynType p+#endif+showThing' _ _ = Nothing++formatType :: DynFlags -> Type -> String+formatType dflag a = showOutputable dflag (removeForAlls a)++tyType :: TyCon -> Maybe String+tyType typ+ | isAlgTyCon typ+ && not (G.isNewTyCon typ)+ && not (G.isClassTyCon typ) = Just "data"+ | G.isNewTyCon typ = Just "newtype"+ | G.isClassTyCon typ = Just "class"+ | Gap.isSynTyCon typ = Just "type"+ | otherwise = Nothing++removeForAlls :: Type -> Type+removeForAlls ty = removeForAlls' ty' tty'+ where+ ty' = dropForAlls ty+ tty' = splitFunTy_maybe ty'++removeForAlls' :: Type -> Maybe (Type, Type) -> Type+removeForAlls' ty Nothing = ty+removeForAlls' ty (Just (pre, ftype))+ | isPredTy pre = mkFunTy pre (dropForAlls ftype)+ | otherwise = ty++showOutputable :: Outputable a => DynFlags -> a -> String+showOutputable dflag = unwords . lines . showPage dflag styleUnqualified . ppr
+ GhcMod/Exe/CaseSplit.hs view
@@ -0,0 +1,261 @@+{-# LANGUAGE CPP #-}++module GhcMod.Exe.CaseSplit (+ splits+ ) where++import Data.List (find, intercalate)+import Data.Maybe (isJust)+import qualified Data.Text as T+import qualified Data.Text.IO as T (readFile)+import System.FilePath+import Prelude++import qualified DataCon as Ty+import GHC (GhcMonad, LPat, Id, ParsedModule(..), TypecheckedModule(..), DynFlags, SrcSpan, Type, GenLocated(L))+import qualified GHC as G+import Outputable (PprStyle)+import qualified TyCon as Ty+import qualified Type as Ty+import Exception++import GhcMod.Convert+import GhcMod.DynFlags+import qualified GhcMod.Gap as Gap+import GhcMod.Monad+import GhcMod.SrcUtils+import GhcMod.Doc+import GhcMod.Logging+import GhcMod.Types+import GhcMod.Utils (withMappedFile)+import GhcMod.FileMapping (fileModSummaryWithMapping)+import Control.DeepSeq++----------------------------------------------------------------+-- CASE SPLITTING+----------------------------------------------------------------++data SplitInfo = SplitInfo G.Name SrcSpan (SrcSpan, Type) [SrcSpan]+ | TySplitInfo G.Name SrcSpan (SrcSpan, Ty.Kind)+data SplitToTextInfo = SplitToTextInfo { sVarName :: String+ , sBindingSpan :: SrcSpan+ , sVarSpan :: SrcSpan+ , sTycons :: [String]+ }++-- | Splitting a variable in a equation.+splits :: IOish m+ => FilePath -- ^ A target file.+ -> Int -- ^ Line number.+ -> Int -- ^ Column number.+ -> GhcModT m String+splits file lineNo colNo =+ ghandle handler $ runGmlT' [Left file] deferErrors $ do+ oopts <- outputOpts+ crdl <- cradle+ style <- getStyle+ dflag <- G.getSessionDynFlags+ modSum <- fileModSummaryWithMapping (cradleCurrentDir crdl </> file)+ whenFound' oopts (getSrcSpanTypeForSplit modSum lineNo colNo) $ \x -> do+ let (varName, bndLoc, (varLoc,varT))+ | (SplitInfo vn bl vlvt _matches) <- x+ = (vn, bl, vlvt)+ | (TySplitInfo vn bl vlvt) <- x+ = (vn, bl, vlvt)+ varName' = showName dflag style varName -- Convert name to string+ t <- withMappedFile file $ \file' ->+ genCaseSplitTextFile file' (SplitToTextInfo varName' bndLoc varLoc $+ getTyCons dflag style varName varT)+ return $!! (fourInts bndLoc, t)+ where+ handler (SomeException ex) = do+ gmLog GmException "splits" $+ text "" $$ nest 4 (showToDoc ex)+ emptyResult =<< outputOpts++----------------------------------------------------------------+-- a. Code for getting the information of the variable++getSrcSpanTypeForSplit :: GhcMonad m => G.ModSummary -> Int -> Int -> m (Maybe SplitInfo)+getSrcSpanTypeForSplit modSum lineNo colNo = do+ fn <- getSrcSpanTypeForFnSplit modSum lineNo colNo+ if isJust fn+ then return fn+ else getSrcSpanTypeForTypeSplit modSum lineNo colNo++-- Information for a function case split+getSrcSpanTypeForFnSplit :: GhcMonad m => G.ModSummary -> Int -> Int -> m (Maybe SplitInfo)+getSrcSpanTypeForFnSplit modSum lineNo colNo = do+ p@ParsedModule{pm_parsed_source = _pms} <- G.parseModule modSum+ tcm@TypecheckedModule{tm_typechecked_source = tcs} <- G.typecheckModule p+ let varPat = find isPatternVar $ listifySpans tcs (lineNo, colNo) :: Maybe (LPat Id)+ match = last $ listifySpans tcs (lineNo, colNo) :: Gap.GLMatchI+ case varPat of+ Nothing -> return Nothing+ Just varPat' -> do+ varT <- Gap.getType tcm varPat' -- Finally we get the type of the var+ case varT of+ Just varT' ->+#if __GLASGOW_HASKELL__ >= 710+ let (L matchL (G.Match _ _ _ (G.GRHSs rhsLs _))) = match+#else+ let (L matchL (G.Match _ _ (G.GRHSs rhsLs _))) = match+#endif+ in return $ Just (SplitInfo (getPatternVarName varPat') matchL varT' (map G.getLoc rhsLs) )+ _ -> return Nothing++isPatternVar :: LPat Id -> Bool+isPatternVar (L _ (G.VarPat _)) = True+isPatternVar _ = False++getPatternVarName :: LPat Id -> G.Name+#if __GLASGOW_HASKELL__ >= 800+getPatternVarName (L _ (G.VarPat (L _ vName))) = G.getName vName+#else+getPatternVarName (L _ (G.VarPat vName)) = G.getName vName+#endif+getPatternVarName _ = error "This should never happened"++-- TODO: Information for a type family case split+getSrcSpanTypeForTypeSplit :: GhcMonad m => G.ModSummary -> Int -> Int -> m (Maybe SplitInfo)+getSrcSpanTypeForTypeSplit _modSum _lineNo _colNo = return Nothing++----------------------------------------------------------------+-- b. Code for getting the possible constructors++getTyCons :: DynFlags -> PprStyle -> G.Name -> G.Type -> [String]+getTyCons dflag style name ty | Just (tyCon, _) <- Ty.splitTyConApp_maybe ty =+ let name' = showName dflag style name -- Convert name to string+ in getTyCon dflag style name' tyCon+getTyCons dflag style name _ = [showName dflag style name]++-- Write cases for one type+getTyCon :: DynFlags -> PprStyle -> String -> Ty.TyCon -> [String]+-- 1. Non-matcheable type constructors+getTyCon _ _ name tyCon | isNotMatcheableTyCon tyCon = [name]+-- 2. Special cases+-- 2.1. Tuples+getTyCon _ _ name tyCon | Ty.isTupleTyCon tyCon =+ let [uniqueDataCon] = Ty.tyConDataCons tyCon+ tupleArity = Ty.dataConSourceArity uniqueDataCon+ -- Deal with both boxed and unboxed tuples+ isUnboxed = Ty.isUnboxedTupleTyCon tyCon+ startSign = if isUnboxed then "(#" else "("+ endSign = if isUnboxed then "#)" else ")"+ in [ startSign ++ intercalate "," (map (\n -> name ++ show n) [1 .. tupleArity]) ++ endSign ]+-- 3. General case+getTyCon dflag style name tyCon = map (getDataCon dflag style name) (Ty.tyConDataCons tyCon)++-- These type constructors should not be matched against+isNotMatcheableTyCon :: Ty.TyCon -> Bool+isNotMatcheableTyCon ty = Ty.isPrimTyCon ty -- Primitive types, such as Int#+ || Ty.isFunTyCon ty -- Function types++-- Write case for one constructor+getDataCon :: DynFlags -> PprStyle -> String -> Ty.DataCon -> String+-- 1. Infix constructors+getDataCon dflag style vName dcon | Ty.dataConIsInfix dcon =+ let dName = showName dflag style $ Ty.dataConName dcon+ in case Ty.dataConSourceArity dcon of+ 0 -> dName+ 1 -> vName ++ dName+ n -> if dName == ":" -- Special case for lists+ then vName ++ ":" ++ vName ++ "s"+ else newVar vName 1 ++ " " ++ dName ++ " " ++ newVars vName 2 (n-1)+-- 2. Non-record, non-infix syntax+getDataCon dflag style vName dcon | [] <- Ty.dataConFieldLabels dcon =+ let dName = showName dflag style $ Ty.dataConName dcon+ in if last dName == '#' -- Special case for I#, C# and so on+ then vName+ else case Ty.dataConSourceArity dcon of+ 0 -> dName+ _ -> dName ++ " " ++ newVarsSpecialSingleton vName 1 (Ty.dataConSourceArity dcon)+-- 3. Records+getDataCon dflag style vName dcon =+ let dName = showName dflag style $ Ty.dataConName dcon+#if __GLASGOW_HASKELL__ >= 800+ flds = map Ty.flSelector $ Ty.dataConFieldLabels dcon+#else+ flds = Ty.dataConFieldLabels dcon+#endif+ in dName ++ " { " ++ showFieldNames dflag style vName flds ++ " }"++-- Create a new variable by adjoining a number+newVar :: String -> Int -> String+newVar v n = v ++ show n++-- Create a list of variables which start with the same prefix+newVars :: String -> Int -> Int -> String+newVars _ _ 0 = ""+newVars v s 1 = newVar v s+newVars v s m = newVar v s ++ " " ++ newVars v (s+1) (m-1)++-- Create a list of variables which start with the same prefix+-- Special case for a single variable, in which case no number is adjoint+newVarsSpecialSingleton :: String -> Int -> Int -> String+newVarsSpecialSingleton v _ 1 = v+newVarsSpecialSingleton v start n = newVars v start n++showFieldNames :: DynFlags -> PprStyle -> String -> [G.Name] -> String+showFieldNames _ _ _ [] = "" -- This should never happen+showFieldNames dflag style v (x:xs) = let fName = showName dflag style x+ fAcc = fName ++ " = " ++ v ++ "_" ++ fName+ in case xs of+ [] -> fAcc+ _ -> fAcc ++ ", " ++ showFieldNames dflag style v xs++----------------------------------------------------------------+-- c. Code for performing the case splitting++genCaseSplitTextFile :: (MonadIO m, GhcMonad m) =>+ FilePath -> SplitToTextInfo -> m String+genCaseSplitTextFile file info = liftIO $ do+ t <- T.readFile file+ return $ getCaseSplitText (T.lines t) info++getCaseSplitText :: [T.Text] -> SplitToTextInfo -> String+getCaseSplitText t SplitToTextInfo{ sVarName = sVN, sBindingSpan = sBS+ , sVarSpan = sVS, sTycons = sT } =+ let bindingText = getBindingText t sBS+ difference = srcSpanDifference sBS sVS+ replaced = map (replaceVarWithTyCon bindingText difference sVN) sT+ -- The newly generated bindings need to be indented to align with the+ -- original binding.+ replaced' = head replaced : map (indentBindingTo sBS) (tail replaced)+ in T.unpack $ T.intercalate (T.pack "\n") (concat replaced')++getBindingText :: [T.Text] -> SrcSpan -> [T.Text]+getBindingText t srcSpan =+ let Just (sl,sc,el,ec) = Gap.getSrcSpan srcSpan+ lines_ = drop (sl - 1) $ take el t+ in if sl == el+ then -- only one line+ [T.drop (sc - 1) $ T.take ec $ head lines_]+ else -- several lines+ let (first,rest,last_) = (head lines_, tail $ init lines_, last lines_)+ in T.drop (sc - 1) first : rest ++ [T.take ec last_]++srcSpanDifference :: SrcSpan -> SrcSpan -> (Int,Int,Int,Int)+srcSpanDifference b v =+ let Just (bsl,bsc,_ ,_) = Gap.getSrcSpan b+ Just (vsl,vsc,vel,vec) = Gap.getSrcSpan v+ in (vsl - bsl, vsc - bsc, vel - bsl, vec - bsc) -- assume variable in one line++replaceVarWithTyCon :: [T.Text] -> (Int,Int,Int,Int) -> String -> String -> [T.Text]+replaceVarWithTyCon t (vsl,vsc,_,vec) varname tycon =+ let tycon' = if ' ' `elem` tycon || ':' `elem` tycon then "(" ++ tycon ++ ")" else tycon+ lengthDiff = length tycon' - length varname+ tycon'' = T.pack $ if lengthDiff < 0 then tycon' ++ replicate (-lengthDiff) ' ' else tycon'+ spacesToAdd = if lengthDiff < 0 then 0 else lengthDiff+ in zipWith (\n line -> if n < vsl+ then line -- before variable starts+ else if n == vsl+ then T.take vsc line `T.append` tycon'' `T.append` T.drop vec line+ else T.replicate spacesToAdd (T.pack " ") `T.append` line)+ [0 ..] t++indentBindingTo :: SrcSpan -> [T.Text] -> [T.Text]+indentBindingTo bndLoc binds =+ let Just (_,sl,_,_) = Gap.getSrcSpan bndLoc+ indent = (T.replicate (sl - 1) (T.pack " ") `T.append`)+ in indent (head binds) : tail binds
+ GhcMod/Exe/Check.hs view
@@ -0,0 +1,57 @@+module GhcMod.Exe.Check (+ checkSyntax+ , check+ , expandTemplate+ , expand+ ) where++import Control.Applicative+import Prelude+import GhcMod.DynFlags+import qualified GhcMod.Gap as Gap+import GhcMod.Logger+import GhcMod.Monad++----------------------------------------------------------------++-- | Checking syntax of a target file using GHC.+-- Warnings and errors are returned.+checkSyntax :: IOish m+ => [FilePath] -- ^ The target files.+ -> GhcModT m String+checkSyntax [] = return ""+checkSyntax files = either id id <$> check files++----------------------------------------------------------------++-- | Checking syntax of a target file using GHC.+-- Warnings and errors are returned.+check :: IOish m+ => [FilePath] -- ^ The target files.+ -> GhcModT m (Either String String)+check files =+ runGmlTWith+ (map Left files)+ return+ ((fmap fst <$>) . withLogger Gap.setNoMaxRelevantBindings)+ (return ())++----------------------------------------------------------------++-- | Expanding Haskell Template.+expandTemplate :: IOish m+ => [FilePath] -- ^ The target files.+ -> GhcModT m String+expandTemplate [] = return ""+expandTemplate files = either id id <$> expand files++----------------------------------------------------------------++-- | Expanding Haskell Template.+expand :: IOish m => [FilePath] -> GhcModT m (Either String String)+expand files =+ runGmlTWith+ (map Left files)+ return+ ((fmap fst <$>) . withLogger (Gap.setDumpSplices . setNoWarningFlags))+ (return ())
+ GhcMod/Exe/Debug.hs view
@@ -0,0 +1,182 @@+module GhcMod.Exe.Debug (debugInfo, rootInfo, componentInfo) where++import Control.Arrow (first)+import Control.Applicative+import Control.Monad+import qualified Data.Map as Map+import qualified Data.Set as Set+import Data.Char+import Data.Maybe+import Data.Version+import Data.List.Split+import System.Directory++import GhcMod.Exe.Internal+import GhcMod.Cradle+import GhcMod.Monad+import GhcMod.Output+import GhcMod.Pretty+import GhcMod.Stack+import GhcMod.Target+import GhcMod.Types+import GhcMod.Utils++import Paths_ghc_mod (version)++import Config (cProjectVersion)+import Pretty++----------------------------------------------------------------++-- | Obtaining debug information.+debugInfo :: IOish m => GhcModT m String+debugInfo = do+ Options {..} <- options+ Cradle {..} <- cradle++ [ghcPath, ghcPkgPath] <- liftIO $+ case cradleProject of+ StackProject se ->+ catMaybes <$> sequence [getStackGhcPath se, getStackGhcPkgPath se]+ _ ->+ return ["ghc", "ghc-pkg"]++ cabal <-+ case cradleProject of+ CabalProject -> cabalDebug ghcPkgPath+ StackProject {} -> (++) <$> stackPaths <*> cabalDebug ghcPkgPath+ _ -> return []++ pkgOpts <- packageGhcOptions++ readProc <- gmReadProcess++ ghcVersion <- liftIO $+ dropWhileEnd isSpace <$> readProc ghcPath ["--numeric-version"] ""++ return $ unlines $+ [ "Version: ghc-mod-" ++ showVersion version+ , "Library GHC Version: " ++ cProjectVersion+ , "System GHC Version: " ++ ghcVersion+ , "Root directory: " ++ cradleRootDir+ , "Current directory: " ++ cradleCurrentDir+ , "GHC Package flags:\n" ++ renderGm (nest 4 $+ fsep $ map text pkgOpts)+ , "GHC System libraries: " ++ ghcLibDir+ ] ++ cabal++stackPaths :: IOish m => GhcModT m [String]+stackPaths = do+ Cradle { cradleProject = StackProject senv } <- cradle+ ghc <- getStackGhcPath senv+ ghcPkg <- getStackGhcPkgPath senv+ return $+ [ "Stack ghc executable: " ++ show ghc+ , "Stack ghc-pkg executable:" ++ show ghcPkg+ ]++cabalDebug :: IOish m => FilePath -> GhcModT m [String]+cabalDebug ghcPkgPath = do+ Cradle {..} <- cradle+ mcs <- cabalResolvedComponents+ let entrypoints = Map.map gmcEntrypoints mcs+ graphs = Map.map gmcHomeModuleGraph mcs+ opts = Map.map gmcGhcOpts mcs+ srcOpts = Map.map gmcGhcSrcOpts mcs++ readProc <- gmReadProcess+ cabalExists <- liftIO $ (/=Nothing) <$> findExecutable "cabal"++ cabalInstVersion <-+ if cabalExists+ then liftIO $+ dropWhileEnd isSpace <$> readProc "cabal" ["--numeric-version"] ""+ else return ""++ packages <- liftIO $ readProc ghcPkgPath ["list", "--simple-output"] ""+ let cabalPackages = filter ((== ["Cabal"]) . take 1 . splitOn "-") $ splitWhen isSpace packages++ return $+ [ "cabal-install Version: " ++ cabalInstVersion+ , "Cabal Library Versions:\n" ++ renderGm (nest 4 $+ fsep $ map text cabalPackages)+ , "Cabal file: " ++ show cradleCabalFile+ , "Project: " ++ show cradleProject+ , "Cabal entrypoints:\n" ++ renderGm (nest 4 $+ mapDoc gmComponentNameDoc smpDoc entrypoints)+ , "Cabal components:\n" ++ renderGm (nest 4 $+ mapDoc gmComponentNameDoc graphDoc graphs)+ , "GHC Cabal options:\n" ++ renderGm (nest 4 $+ mapDoc gmComponentNameDoc (fsep . map text) opts)+ , "GHC search path options:\n" ++ renderGm (nest 4 $+ mapDoc gmComponentNameDoc (fsep . map text) srcOpts)+ ]++componentInfo :: IOish m => [String] -> GhcModT m String+componentInfo ts = do+ -- TODO: most of this is copypasta of targetGhcOptions. Factor out more+ -- useful function from there.+ crdl <- cradle+ sefnmn <- Set.fromList `liftM` mapM guessModuleFile ts+ mcs <- cabalResolvedComponents+ let+ mdlcs = moduleComponents mcs `zipMap` Set.toList sefnmn+ candidates = findCandidates $ map snd mdlcs+ cn = pickComponent candidates+ opts <- targetGhcOptions crdl sefnmn++ return $ unlines $+ [ "Matching Components:\n" ++ renderGm (nest 4 $+ alistDoc (either text mnDoc) (setDoc gmComponentNameDoc) mdlcs)+ , "Picked Component:\n" ++ renderGm (nest 4 $+ gmComponentNameDoc cn)+ , "GHC Cabal options:\n" ++ renderGm (nest 4 $ fsep $ map text opts)+ ]+ where+ zipMap f l = l `zip` (f `map` l)++guessModuleFile :: MonadIO m => String -> m (Either FilePath ModuleName)+guessModuleFile m+ | (isUpper . head .&&. (all $ all $ isAlphaNum .||. (=='.')) . splitOn ".") m =+ return $ Right $ mkModuleName m+ where+ infixr 1 .||.+ infixr 2 .&&.+ (.||.) = liftA2 (||)+ (.&&.) = liftA2 (&&)++guessModuleFile str = Left `liftM` liftIO (canonFilePath str)++graphDoc :: GmModuleGraph -> Doc+graphDoc GmModuleGraph{..} =+ mapDoc mpDoc smpDoc' gmgGraph+ where+ smpDoc' smp = vcat $ map mpDoc' $ Set.toList smp+ mpDoc' = text . moduleNameString . mpModule++setDoc :: (a -> Doc) -> Set.Set a -> Doc+setDoc f s = vcat $ map f $ Set.toList s++smpDoc :: Set.Set ModulePath -> Doc+smpDoc smp = setDoc mpDoc smp++mpDoc :: ModulePath -> Doc+mpDoc (ModulePath mn fn) = text (moduleNameString mn) <+> parens (text fn)++mnDoc :: ModuleName -> Doc+mnDoc mn = text (moduleNameString mn)++alistDoc :: Ord k => (k -> Doc) -> (a -> Doc) -> [(k, a)] -> Doc+alistDoc fk fa alist = mapDoc fk fa (Map.fromList alist)++mapDoc :: (k -> Doc) -> (a -> Doc) -> Map.Map k a -> Doc+mapDoc kd ad m = vcat $+ map (uncurry ($+$)) $ map (first kd) $ Map.toList $ Map.map (nest 4 . ad) m+----------------------------------------------------------------++-- | Obtaining root information.+rootInfo :: forall m. (IOish m, GmOut m, GmEnv m) => m String+rootInfo = do+ crdl <- findCradleNoLog =<< (optPrograms <$> options)+ liftIO $ cleanupCradle crdl+ return $ cradleRootDir crdl ++ "\n"
+ GhcMod/Exe/FillSig.hs view
@@ -0,0 +1,590 @@+{-# LANGUAGE CPP, MultiParamTypeClasses, FunctionalDependencies #-}+{-# LANGUAGE FlexibleInstances #-}++module GhcMod.Exe.FillSig (+ sig+ , refine+ , auto+ ) where++import Data.Char (isSymbol)+import Data.Function (on)+import Data.Functor+import Data.List (find, nub, sortBy)+import qualified Data.Map as M+import Data.Maybe (catMaybes)+import Prelude++import Exception (ghandle, SomeException(..))+import GHC (GhcMonad, Id, ParsedModule(..), TypecheckedModule(..), DynFlags,+ SrcSpan, Type, GenLocated(L))+import Pretty (($$), text, nest)+import qualified GHC as G+import qualified Name as G+import Outputable (PprStyle)+import qualified Type as Ty+import qualified HsBinds as Ty+import qualified Class as Ty+import qualified Var as Ty+import qualified HsPat as Ty+import qualified Language.Haskell.Exts as HE+import Djinn.GHC++import qualified GhcMod.Gap as Gap+import GhcMod.Convert+import GhcMod.DynFlags+import GhcMod.Monad+import GhcMod.SrcUtils+import GhcMod.Logging (gmLog)+import GhcMod.Pretty (showToDoc)+import GhcMod.Doc+import GhcMod.Types+import GhcMod.FileMapping (fileModSummaryWithMapping)++#if __GLASGOW_HASKELL__ >= 710+import GHC (unLoc)+#endif++----------------------------------------------------------------+-- INTIAL CODE FROM FUNCTION OR INSTANCE SIGNATURE+----------------------------------------------------------------++-- Possible signatures we can find: function or instance+data SigInfo+ = Signature SrcSpan [G.RdrName] (G.HsType G.RdrName)+ | InstanceDecl SrcSpan G.Class+ | TyFamDecl SrcSpan G.RdrName TyFamType {- True if closed -} [G.RdrName]++-- Signature for fallback operation via haskell-src-exts+data HESigInfo+ = HESignature HE.SrcSpan [HE.Name HE.SrcSpanInfo] (HE.Type HE.SrcSpanInfo)+ | HEFamSignature+ HE.SrcSpan+ TyFamType+ (HE.Name HE.SrcSpanInfo)+ [HE.Name HE.SrcSpanInfo]++data TyFamType = Closed | Open | Data+initialTyFamString :: TyFamType -> (String, String)+initialTyFamString Closed = ("instance", "")+initialTyFamString Open = ("function", "type instance ")+initialTyFamString Data = ("function", "data instance ")++-- | Create a initial body from a signature.+sig :: IOish m+ => FilePath -- ^ A target file.+ -> Int -- ^ Line number.+ -> Int -- ^ Column number.+ -> GhcModT m String+sig file lineNo colNo =+ runGmlT' [Left file] deferErrors $ ghandle fallback $ do+ oopts <- outputOpts+ style <- getStyle+ dflag <- G.getSessionDynFlags+ modSum <- fileModSummaryWithMapping file+ whenFound oopts (getSignature modSum lineNo colNo) $ \s ->+ case s of+ Signature loc names ty ->+ ("function", fourInts loc, map (initialBody dflag style ty) names)++ InstanceDecl loc cls ->+ let body x = initialBody dflag style (G.idType x) x+ in ("instance", fourInts loc, body `map` Ty.classMethods cls)++ TyFamDecl loc name flavour vars ->+ let (rTy, initial) = initialTyFamString flavour+ body = initialFamBody dflag style name vars+ in (rTy, fourInts loc, [initial ++ body])+ where+ fallback (SomeException _) = do+ oopts <- outputOpts+ -- Code cannot be parsed by ghc module+ -- Fallback: try to get information via haskell-src-exts+ whenFound oopts (getSignatureFromHE file lineNo colNo) $ \x -> case x of+ HESignature loc names ty ->+ ("function", fourIntsHE loc, map (initialBody undefined undefined ty) names)+ HEFamSignature loc flavour name vars ->+ let (rTy, initial) = initialTyFamString flavour+ in (rTy, fourIntsHE loc, [initial ++ initialFamBody undefined undefined name vars])++----------------------------------------------------------------+-- a. Code for getting the information++-- Get signature from ghc parsing and typechecking+getSignature :: GhcMonad m => G.ModSummary -> Int -> Int -> m (Maybe SigInfo)+getSignature modSum lineNo colNo = do+ p@ParsedModule{pm_parsed_source = ps} <- G.parseModule modSum+ -- Inspect the parse tree to find the signature+ case listifyParsedSpans ps (lineNo, colNo) :: [G.LHsDecl G.RdrName] of+#if __GLASGOW_HASKELL__ >= 800+ [L loc (G.SigD (Ty.TypeSig names (G.HsIB _ (G.HsWC _ _ (L _ ty)))))] ->+#elif __GLASGOW_HASKELL__ >= 710+ [L loc (G.SigD (Ty.TypeSig names (L _ ty) _))] ->+#else+ [L loc (G.SigD (Ty.TypeSig names (L _ ty)))] ->+#endif+ -- We found a type signature+ return $ Just $ Signature loc (map G.unLoc names) ty+ [L _ (G.InstD _)] -> do+ -- We found an instance declaration+ TypecheckedModule{tm_renamed_source = Just tcs+ ,tm_checked_module_info = minfo} <- G.typecheckModule p+ let lst = listifyRenamedSpans tcs (lineNo, colNo)+ case Gap.getClass lst of+ Just (clsName,loc) -> obtainClassInfo minfo clsName loc+ _ -> return Nothing+#if __GLASGOW_HASKELL__ >= 800+ [L loc (G.TyClD (G.FamDecl (G.FamilyDecl info (L _ name) (G.HsQTvs _ vars _) _ _)))] -> do+#elif __GLASGOW_HASKELL__ >= 708+ [L loc (G.TyClD (G.FamDecl (G.FamilyDecl info (L _ name) (G.HsQTvs _ vars) _)))] -> do+#elif __GLASGOW_HASKELL__ >= 706+ [L loc (G.TyClD (G.TyFamily info (L _ name) (G.HsQTvs _ vars) _))] -> do+#else+ [L loc (G.TyClD (G.TyFamily info (L _ name) vars _))] -> do+#endif+#if __GLASGOW_HASKELL__ >= 708+ let flavour = case info of+ G.ClosedTypeFamily _ -> Closed+ G.OpenTypeFamily -> Open+ G.DataFamily -> Data+#else+ let flavour = case info of -- Closed type families where introduced in GHC 7.8+ G.TypeFamily -> Open+ G.DataFamily -> Data+#endif++#if __GLASGOW_HASKELL__ >= 800+ getTyFamVarName x = case x of+ L _ (G.UserTyVar (G.L _ n)) -> n+ L _ (G.KindedTyVar (G.L _ n) _) -> n+#elif __GLASGOW_HASKELL__ >= 710+ getTyFamVarName x = case x of+ L _ (G.UserTyVar n) -> n+ L _ (G.KindedTyVar (G.L _ n) _) -> n+#elif __GLASGOW_HASKELL__ >= 706+ getTyFamVarName x = case x of+ L _ (G.UserTyVar n) -> n+ L _ (G.KindedTyVar n _) -> n+#else+ getTyFamVarName x = case x of -- In GHC 7.4, HsTyVarBndr's have an extra arg+ L _ (G.UserTyVar n _) -> n+ L _ (G.KindedTyVar n _ _) -> n+#endif+ in return $ Just (TyFamDecl loc name flavour $ map getTyFamVarName vars)+ _ -> return Nothing+ where obtainClassInfo :: GhcMonad m => G.ModuleInfo -> G.Name -> SrcSpan -> m (Maybe SigInfo)+ obtainClassInfo minfo clsName loc = do+ tyThing <- G.modInfoLookupName minfo clsName+ return $ do Ty.ATyCon clsCon <- tyThing -- In Maybe+ cls <- G.tyConClass_maybe clsCon+ return $ InstanceDecl loc cls++-- Get signature from haskell-src-exts+getSignatureFromHE :: (MonadIO m, GhcMonad m) =>+ FilePath -> Int -> Int -> m (Maybe HESigInfo)+getSignatureFromHE file lineNo colNo = do+ presult <- liftIO $ HE.parseFile file+ return $ case presult of+ HE.ParseOk (HE.Module _ _ _ _ mdecls) -> do+ decl <- find (typeSigInRangeHE lineNo colNo) mdecls+ case decl of+ HE.TypeSig (HE.SrcSpanInfo s _) names ty ->+ return $ HESignature s names ty++ HE.TypeFamDecl (HE.SrcSpanInfo s _) declHead _ _ ->+ let (name, tys) = dHeadTyVars declHead in+ return $ HEFamSignature s Open name (map cleanTyVarBind tys)++ HE.DataFamDecl (HE.SrcSpanInfo s _) _ declHead _ ->+ let (name, tys) = dHeadTyVars declHead in+ return $ HEFamSignature s Open name (map cleanTyVarBind tys)+ _ -> fail ""+ _ -> Nothing+ where cleanTyVarBind (HE.KindedVar _ n _) = n+ cleanTyVarBind (HE.UnkindedVar _ n) = n++#if MIN_VERSION_haskell_src_exts(1,16,0)+ dHeadTyVars :: HE.DeclHead l -> (HE.Name l, [HE.TyVarBind l])+ dHeadTyVars (HE.DHead _ name) = (name, [])+ dHeadTyVars (HE.DHApp _ r ty) = (++[ty]) `fmap` (dHeadTyVars r)+ dHeadTyVars (HE.DHInfix _ ty name) = (name, [ty])+ dHeadTyVars (HE.DHParen _ r) = dHeadTyVars r+#else+ dHeadTyVars :: HE.DeclHead l -> (HE.Name l, [HE.TyVarBind l])+ dHeadTyVars (HE.DHead _ n tys) = (n, tys)+#endif++----------------------------------------------------------------+-- b. Code for generating initial code++-- A list of function arguments, and whether they are functions or normal+-- arguments is built from either a function signature or an instance signature+data FnArg = FnArgFunction | FnArgNormal | FnExplicitName String++initialBody :: FnArgsInfo ty name => DynFlags -> PprStyle -> ty -> name -> String+initialBody dflag style ty name =+ initialBody' (getFnName dflag style name) (getFnArgs ty)++initialBody' :: String -> [FnArg] -> String+initialBody' fname args =+ initialHead fname args ++ " = " ++ n ++ "_body"+ where n = if isSymbolName fname then "" else '_':fname++initialFamBody :: FnArgsInfo ty name+ => DynFlags -> PprStyle -> name -> [name] -> String+initialFamBody dflag style name args =+ initialHead fnName fnArgs ++ " = ()"+ where fnName = getFnName dflag style name+ fnArgs = map (FnExplicitName . getFnName dflag style) args++initialHead :: String -> [FnArg] -> String+initialHead fname args =+ case initialBodyArgs args infiniteVars infiniteFns of+ [] -> fname+ arglist -> if isSymbolName fname+ then head arglist ++ " " ++ fname ++ " " ++ unwords (tail arglist)+ else fname ++ " " ++ unwords arglist++initialBodyArgs :: [FnArg] -> [String] -> [String] -> [String]+initialBodyArgs [] _ _ = []+initialBodyArgs (FnArgFunction:xs) vs (f:fs) = f : initialBodyArgs xs vs fs+initialBodyArgs (FnArgNormal:xs) (v:vs) fs = v : initialBodyArgs xs vs fs+initialBodyArgs (FnExplicitName n:xs) vs fs = n : initialBodyArgs xs vs fs+initialBodyArgs _ _ _ =+ error "initialBodyArgs: This should never happen" -- Lists are infinite++initialHead1 :: String -> [FnArg] -> [String] -> String+initialHead1 fname args elts =+ case initialBodyArgs1 args elts of+ [] -> fname+ arglist+ | isSymbolName fname ->+ head arglist ++ " " ++ fname ++ " " ++ unwords (tail arglist)+ | otherwise ->+ fname ++ " " ++ unwords arglist++initialBodyArgs1 :: [FnArg] -> [String] -> [String]+initialBodyArgs1 args elts = take (length args) elts++-- Getting the initial body of function and instances differ+-- This is because for functions we only use the parsed file+-- (so the full file doesn't have to be type correct)+-- but for instances we need to get information about the class++class FnArgsInfo ty name | ty -> name, name -> ty where+ getFnName :: DynFlags -> PprStyle -> name -> String+ getFnArgs :: ty -> [FnArg]++instance FnArgsInfo (G.HsType G.RdrName) (G.RdrName) where+ getFnName dflag style name = showOccName dflag style $ Gap.occName name+#if __GLASGOW_HASKELL__ >= 800+ getFnArgs (G.HsForAllTy _ (L _ iTy))+#elif __GLASGOW_HASKELL__ >= 710+ getFnArgs (G.HsForAllTy _ _ _ _ (L _ iTy))+#else+ getFnArgs (G.HsForAllTy _ _ _ (L _ iTy))+#endif+ = getFnArgs iTy++ getFnArgs (G.HsParTy (L _ iTy)) = getFnArgs iTy+ getFnArgs (G.HsFunTy (L _ lTy) (L _ rTy)) =+ (if fnarg lTy then FnArgFunction else FnArgNormal):getFnArgs rTy+ where fnarg ty = case ty of+#if __GLASGOW_HASKELL__ >= 800+ (G.HsForAllTy _ (L _ iTy)) ->+#elif __GLASGOW_HASKELL__ >= 710+ (G.HsForAllTy _ _ _ _ (L _ iTy)) ->+#else+ (G.HsForAllTy _ _ _ (L _ iTy)) ->+#endif+ fnarg iTy++ (G.HsParTy (L _ iTy)) -> fnarg iTy+ (G.HsFunTy _ _) -> True+ _ -> False+ getFnArgs _ = []++instance FnArgsInfo (HE.Type HE.SrcSpanInfo) (HE.Name HE.SrcSpanInfo) where+ getFnName _ _ (HE.Ident _ s) = s+ getFnName _ _ (HE.Symbol _ s) = s+ getFnArgs (HE.TyForall _ _ _ iTy) = getFnArgs iTy+ getFnArgs (HE.TyParen _ iTy) = getFnArgs iTy+ getFnArgs (HE.TyFun _ lTy rTy) =+ (if fnarg lTy then FnArgFunction else FnArgNormal):getFnArgs rTy+ where fnarg ty = case ty of+ (HE.TyForall _ _ _ iTy) -> fnarg iTy+ (HE.TyParen _ iTy) -> fnarg iTy+ (HE.TyFun _ _ _) -> True+ _ -> False+ getFnArgs _ = []++instance FnArgsInfo Type Id where+ getFnName dflag style method = showOccName dflag style $ G.getOccName method+ getFnArgs = getFnArgs' . Ty.dropForAlls+ where getFnArgs' ty | Just (lTy,rTy) <- Ty.splitFunTy_maybe ty =+ maybe (if Ty.isPredTy lTy then getFnArgs' rTy else FnArgNormal:getFnArgs' rTy)+ (\_ -> FnArgFunction:getFnArgs' rTy)+ $ Ty.splitFunTy_maybe lTy++ getFnArgs' ty | Just (_,iTy) <- Ty.splitForAllTy_maybe ty =+ getFnArgs' iTy++ getFnArgs' _ = []++-- Infinite supply of variable and function variable names+infiniteVars, infiniteFns :: [String]+infiniteVars = infiniteSupply ["x","y","z","t","u","v","w"]+infiniteFns = infiniteSupply ["f","g","h"]+infiniteSupply :: [String] -> [String]+infiniteSupply initialSupply =+ initialSupply ++ concatMap (\n -> map (\v -> v ++ show n) initialSupply)+ ([1 .. ] :: [Integer])++-- Check whether a String is a symbol name+isSymbolName :: String -> Bool+isSymbolName (c:_) = c `elem` "!#$%&*+./<=>?@\\^|-~" || isSymbol c+isSymbolName [] = error "This should never happen"+++----------------------------------------------------------------+-- REWRITE A HOLE / UNDEFINED VIA A FUNCTION+----------------------------------------------------------------++refine :: IOish m+ => FilePath -- ^ A target file.+ -> Int -- ^ Line number.+ -> Int -- ^ Column number.+ -> Expression -- ^ A Haskell expression.+ -> GhcModT m String+refine file lineNo colNo (Expression expr) =+ ghandle handler $+ runGmlT' [Left file] deferErrors $ do+ oopts <- outputOpts+ style <- getStyle+ dflag <- G.getSessionDynFlags+ modSum <- fileModSummaryWithMapping file+ p <- G.parseModule modSum+ tcm@TypecheckedModule{tm_typechecked_source = tcs} <- G.typecheckModule p+ ety <- G.exprType expr+ whenFound oopts (findVar dflag style tcm tcs lineNo colNo) $+ \(loc, name, rty, paren) ->+ let eArgs = getFnArgs ety+ rArgs = getFnArgs rty+ diffArgs' = length eArgs - length rArgs+ diffArgs = if diffArgs' < 0 then 0 else diffArgs'+ iArgs = take diffArgs eArgs+ txt = initialHead1 expr iArgs (infinitePrefixSupply name)+ in (fourInts loc, doParen paren txt)+ where+ handler (SomeException ex) = do+ gmLog GmException "refining" $+ text "" $$ nest 4 (showToDoc ex)+ emptyResult =<< outputOpts++-- Look for the variable in the specified position+findVar+ :: GhcMonad m+ => DynFlags+ -> PprStyle+ -> G.TypecheckedModule+ -> G.TypecheckedSource+ -> Int+ -> Int+ -> m (Maybe (SrcSpan, String, Type, Bool))+findVar dflag style tcm tcs lineNo colNo =+ case lst of+#if __GLASGOW_HASKELL__ >= 800+ e@(L _ (G.HsVar (L _ i))):others -> do+#else+ e@(L _ (G.HsVar i)):others -> do+#endif+ tyInfo <- Gap.getType tcm e+ case tyInfo of+ Just (s, typ)+ | name == "undefined" || head name == '_' ->+ return $ Just (s, name, typ, b)+ where+ name = getFnName dflag style i+ -- If inside an App, we need parenthesis+ b = case others of+ L _ (G.HsApp (L _ a1) (L _ a2)):_ ->+ isSearchedVar i a1 || isSearchedVar i a2+ _ -> False+ _ -> return Nothing+ _ -> return Nothing+ where+ lst :: [G.LHsExpr Id]+ lst = sortBy (cmp `on` G.getLoc) $ listifySpans tcs (lineNo, colNo)++infinitePrefixSupply :: String -> [String]+infinitePrefixSupply "undefined" = repeat "undefined"+infinitePrefixSupply p = map (\n -> p ++ "_" ++ show n) ([1 ..] :: [Integer])++doParen :: Bool -> String -> String+doParen False s = s+doParen True s = if ' ' `elem` s then '(':s ++ ")" else s++isSearchedVar :: Id -> G.HsExpr Id -> Bool+#if __GLASGOW_HASKELL__ >= 800+isSearchedVar i (G.HsVar (L _ i2)) = i == i2+#else+isSearchedVar i (G.HsVar i2) = i == i2+#endif+isSearchedVar _ _ = False+++----------------------------------------------------------------+-- REFINE AUTOMATICALLY+----------------------------------------------------------------++auto :: IOish m+ => FilePath -- ^ A target file.+ -> Int -- ^ Line number.+ -> Int -- ^ Column number.+ -> GhcModT m String+auto file lineNo colNo =+ ghandle handler $ runGmlT' [Left file] deferErrors $ do+ oopts <- outputOpts+ style <- getStyle+ dflag <- G.getSessionDynFlags+ modSum <- fileModSummaryWithMapping file+ p <- G.parseModule modSum+ tcm@TypecheckedModule {+ tm_typechecked_source = tcs+ , tm_checked_module_info = minfo+ } <- G.typecheckModule p+ whenFound' oopts (findVar dflag style tcm tcs lineNo colNo) $ \(loc, _name, rty, paren) -> do+ topLevel <- getEverythingInTopLevel minfo+ let (f,pats) = getPatsForVariable tcs (lineNo,colNo)+ -- Remove self function to prevent recursion, and id to trim+ -- cases+ filterFn (n,_) = let funName = G.getOccString n+ recName = G.getOccString (G.getName f)+ in funName `notElem` recName:notWantedFuns+ -- Find without using other functions in top-level+ localBnds = M.unions $+ map (\(L _ pat) -> getBindingsForPat pat) pats+ lbn = filter filterFn (M.toList localBnds)+ djinnsEmpty <- djinn True (Just minfo) lbn rty (Max 10) 100000+ let -- Find with the entire top-level+ almostEnv = M.toList $ M.union localBnds topLevel+ env = filter filterFn almostEnv+ djinns <- djinn True (Just minfo) env rty (Max 10) 100000+ return ( fourInts loc+ , map (doParen paren) $ nub (djinnsEmpty ++ djinns))+ where+ handler (SomeException ex) = do+ gmLog GmException "auto-refining" $+ text "" $$ nest 4 (showToDoc ex)+ emptyResult =<< outputOpts++-- Functions we do not want in completions+notWantedFuns :: [String]+notWantedFuns = ["id", "asTypeOf", "const"]++-- Get all things defined in top-level+getEverythingInTopLevel :: GhcMonad m => G.ModuleInfo -> m (M.Map G.Name Type)+getEverythingInTopLevel m = do+ let modInfo = tyThingsToInfo (G.modInfoTyThings m)+ topNames = G.modInfoTopLevelScope m+ case topNames of+ Just topNames' -> do topThings <- mapM G.lookupGlobalName topNames'+ let topThings' = catMaybes topThings+ topInfo = tyThingsToInfo topThings'+ return $ M.union modInfo topInfo+ Nothing -> return modInfo++tyThingsToInfo :: [Ty.TyThing] -> M.Map G.Name Type+tyThingsToInfo [] = M.empty+tyThingsToInfo (G.AnId i : xs) =+ M.insert (G.getName i) (Ty.varType i) (tyThingsToInfo xs)+-- Getting information about constructors is not needed+-- because they will be added by djinn-ghc when traversing types+-- #if __GLASGOW_HASKELL__ >= 708+-- tyThingToInfo (G.AConLike (G.RealDataCon con)) = return [(Ty.dataConName con, Ty.dataConUserType con)]+-- #else+-- tyThingToInfo (G.AConLike con) = return [(Ty.dataConName con, Ty.dataConUserType con)]+-- #endif+tyThingsToInfo (_:xs) = tyThingsToInfo xs++-- Find the Id of the function and the pattern where the hole is located+getPatsForVariable :: G.TypecheckedSource -> (Int,Int) -> (Id, [Ty.LPat Id])+getPatsForVariable tcs (lineNo, colNo) =+ let (L _ bnd:_) = sortBy (cmp `on` G.getLoc) $+ listifySpans tcs (lineNo, colNo) :: [G.LHsBind Id]+ in case bnd of+ G.PatBind { Ty.pat_lhs = L ploc pat } -> case pat of+ Ty.ConPatIn (L _ i) _ -> (i, [L ploc pat])+ _ -> (error "This should never happen", [])+ G.FunBind { Ty.fun_id = L _ funId } ->+ let m = sortBy (cmp `on` G.getLoc) $ listifySpans tcs (lineNo, colNo)+#if __GLASGOW_HASKELL__ >= 708+ :: [G.LMatch Id (G.LHsExpr Id)]+#else+ :: [G.LMatch Id]+#endif+#if __GLASGOW_HASKELL__ >= 710+ (L _ (G.Match _ pats _ _):_) = m+#else+ (L _ (G.Match pats _ _):_) = m+#endif+ in (funId, pats)+ _ -> (error "This should never happen", [])++getBindingsForPat :: Ty.Pat Id -> M.Map G.Name Type+#if __GLASGOW_HASKELL__ >= 800+getBindingsForPat (Ty.VarPat (L _ i)) = M.singleton (G.getName i) (Ty.varType i)+#else+getBindingsForPat (Ty.VarPat i) = M.singleton (G.getName i) (Ty.varType i)+#endif+getBindingsForPat (Ty.LazyPat (L _ l)) = getBindingsForPat l+getBindingsForPat (Ty.BangPat (L _ b)) = getBindingsForPat b+getBindingsForPat (Ty.AsPat (L _ a) (L _ i)) =+ M.insert (G.getName a) (Ty.varType a) (getBindingsForPat i)+#if __GLASGOW_HASKELL__ >= 708+getBindingsForPat (Ty.ListPat l _ _) =+ M.unions $ map (\(L _ i) -> getBindingsForPat i) l+#else+getBindingsForPat (Ty.ListPat l _) =+ M.unions $ map (\(L _ i) -> getBindingsForPat i) l+#endif+getBindingsForPat (Ty.TuplePat l _ _) =+ M.unions $ map (\(L _ i) -> getBindingsForPat i) l+getBindingsForPat (Ty.PArrPat l _) =+ M.unions $ map (\(L _ i) -> getBindingsForPat i) l+getBindingsForPat (Ty.ViewPat _ (L _ i) _) = getBindingsForPat i+getBindingsForPat (Ty.SigPatIn (L _ i) _) = getBindingsForPat i+getBindingsForPat (Ty.SigPatOut (L _ i) _) = getBindingsForPat i+getBindingsForPat (Ty.ConPatIn (L _ i) d) =+ M.insert (G.getName i) (Ty.varType i) (getBindingsForRecPat d)+getBindingsForPat (Ty.ConPatOut { Ty.pat_args = d }) = getBindingsForRecPat d+getBindingsForPat _ = M.empty++getBindingsForRecPat :: Ty.HsConPatDetails Id -> M.Map G.Name Type+#if __GLASGOW_HASKELL__ >= 800+getBindingsForRecPat (G.PrefixCon args) =+#else+getBindingsForRecPat (Ty.PrefixCon args) =+#endif+ M.unions $ map (\(L _ i) -> getBindingsForPat i) args+#if __GLASGOW_HASKELL__ >= 800+getBindingsForRecPat (G.InfixCon (L _ a1) (L _ a2)) =+#else+getBindingsForRecPat (Ty.InfixCon (L _ a1) (L _ a2)) =+#endif+ M.union (getBindingsForPat a1) (getBindingsForPat a2)+#if __GLASGOW_HASKELL__ >= 800+getBindingsForRecPat (G.RecCon (Ty.HsRecFields { Ty.rec_flds = fields })) =+#else+getBindingsForRecPat (Ty.RecCon (Ty.HsRecFields { Ty.rec_flds = fields })) =+#endif+ getBindingsForRecFields (map unLoc' fields)+ where+#if __GLASGOW_HASKELL__ >= 710+ unLoc' = unLoc+#else+ unLoc' = id+#endif+ getBindingsForRecFields [] = M.empty+ getBindingsForRecFields (Ty.HsRecField {Ty.hsRecFieldArg = (L _ a)}:fs) =+ M.union (getBindingsForPat a) (getBindingsForRecFields fs)
+ GhcMod/Exe/Find.hs view
@@ -0,0 +1,219 @@+{-# LANGUAGE CPP, BangPatterns, TupleSections, DeriveGeneric #-}++module GhcMod.Exe.Find+ ( Symbol+ , SymbolDb+ , loadSymbolDb+ , lookupSymbol+ , dumpSymbol+ , findSymbol+ , lookupSym+ , isOutdated+ -- * Load 'SymbolDb' asynchronously+ , AsyncSymbolDb+ , newAsyncSymbolDb+ , getAsyncSymbolDb+ ) where++import qualified GHC as G+import FastString+import Module+import OccName+import HscTypes+import Exception++import GhcMod.Convert+import GhcMod.Gap+import GhcMod.Monad+import GhcMod.Output+import GhcMod.Types+import GhcMod.Utils+import GhcMod.World+import GhcMod.LightGhc++import Control.Applicative+import Control.DeepSeq+import Control.Monad+import Control.Monad.Trans.Control+import Control.Concurrent++import Data.List+import Data.Binary+import Data.Function+import qualified Data.ByteString as BS+import qualified Data.ByteString.Lazy as LBS+import Data.IORef++import System.Directory.ModTime+import System.IO.Unsafe++import GHC.Generics (Generic)++import Data.Map (Map)+import qualified Data.Map as M+import Data.Set (Set)+import qualified Data.Set as S+import GhcMod.PathsAndFiles+import System.Directory+import Prelude++----------------------------------------------------------------++-- | Type of function and operation names.+type Symbol = BS.ByteString+type ModuleNameBS = BS.ByteString++-- | Database from 'Symbol' to \['ModuleString'\].+data SymbolDb = SymbolDb+ { sdTable :: Map Symbol (Set ModuleNameBS)+ , sdTimestamp :: ModTime+ } deriving (Generic)++#if __GLASGOW_HASKELL__ >= 708+instance Binary SymbolDb+#else+instance Binary SymbolDb where+ put (SymbolDb a b) = put a >> put b+ get = do+ a <- get+ b <- get+ return (SymbolDb a b)+#endif+instance NFData SymbolDb++isOutdated :: IOish m => SymbolDb -> GhcModT m Bool+isOutdated db =+ isOlderThan (sdTimestamp db) <$> timedPackageCaches++----------------------------------------------------------------++-- | Looking up 'SymbolDb' with 'Symbol' to \['ModuleString'\]+-- which will be concatenated. 'loadSymbolDb' is called internally.+findSymbol :: IOish m => String -> GhcModT m String+findSymbol sym = loadSymbolDb' >>= lookupSymbol sym++-- | Looking up 'SymbolDb' with 'Symbol' to \['ModuleString'\]+-- which will be concatenated.+lookupSymbol :: IOish m => String -> SymbolDb -> GhcModT m String+lookupSymbol sym db = convert' $ lookupSym (fastStringToByteString $ mkFastString sym) db++lookupSym :: Symbol -> SymbolDb -> [ModuleString]+lookupSym sym db = map (ModuleString . unpackFS . mkFastStringByteString') $ S.toList $ M.findWithDefault S.empty sym $ sdTable db++---------------------------------------------------------------++loadSymbolDb' :: IOish m => GhcModT m SymbolDb+loadSymbolDb' = do+ cache <- symbolCache <$> cradle+ let doLoad True = do+ db <- decode <$> liftIO (LBS.readFile cache)+ outdated <- isOutdated db+ if outdated+ then doLoad False+ else return db+ doLoad False = do+ db <- loadSymbolDb+ liftIO $ LBS.writeFile cache $ encode db+ return db+ doLoad =<< liftIO (doesFileExist cache)++-- | Loading a file and creates 'SymbolDb'.+loadSymbolDb :: IOish m => GhcModT m SymbolDb+loadSymbolDb = do+ ghcMod <- liftIO ghcModExecutable+ readProc <- gmReadProcess'+ out <- liftIO $ readProc ghcMod ["--verbose", "error", "dumpsym"] ""+ return $!! decode out++----------------------------------------------------------------+-- used 'ghc-mod dumpsym'++-- | Dumps a 'Binary' representation of 'SymbolDb' to stdout+dumpSymbol :: IOish m => GhcModT m ()+dumpSymbol = do+ ts <- liftIO getCurrentModTime+ st <- runGmPkgGhc $ getGlobalSymbolTable+ liftIO . LBS.putStr $ encode SymbolDb {+ sdTable = st+ , sdTimestamp = ts+ }++-- | Check whether given file is older than any file from the given set.+-- Returns True if given file does not exist.+isOlderThan :: ModTime -> [TimedFile] -> Bool+isOlderThan tCache files =+ any (tCache <=) $ map tfTime files -- including equal just in case++-- | Browsing all functions in all system modules.+getGlobalSymbolTable :: (G.GhcMonad m, MonadIO m)+ => m (Map Symbol (Set ModuleNameBS))+getGlobalSymbolTable =+ foldM extend M.empty =<< (listVisibleModules <$> G.getSessionDynFlags)++extend :: (G.GhcMonad m, MonadIO m)+ => Map Symbol (Set ModuleNameBS)+ -> Module+ -> m (Map Symbol (Set ModuleNameBS))+extend mm mdl = do+ hsc_env <- G.getSession+ eps <- liftIO $ readIORef $ hsc_EPS hsc_env+ modinfo <- liftIO $ unsafeInterleaveIO $ runLightGhc hsc_env $ do+ G.getModuleInfo mdl <* liftIO (writeIORef (hsc_EPS hsc_env) eps)+ return $ M.unionWith S.union mm $ extractBindings modinfo mdl++extractBindings :: Maybe G.ModuleInfo+ -> G.Module+ -> Map Symbol (Set ModuleNameBS)+extractBindings Nothing _ = M.empty+extractBindings (Just inf) mdl = M.fromList $ do+ name <- G.modInfoExports inf+ let sym = fastStringToByteString $ occNameFS $ G.getOccName name+ mdls = S.singleton $ fastStringToByteString $ moduleNameFS $ moduleName mdl+ return (sym, mdls)++mkFastStringByteString' :: BS.ByteString -> FastString+#if !MIN_VERSION_ghc(7,8,0)+fastStringToByteString :: FastString -> BS.ByteString+fastStringToByteString = BS.pack . bytesFS++mkFastStringByteString' = mkFastStringByteList . BS.unpack+#elif __GLASGOW_HASKELL__ == 708+mkFastStringByteString' = unsafePerformIO . mkFastStringByteString+#else+mkFastStringByteString' = mkFastStringByteString+#endif++----------------------------------------------------------------++data AsyncSymbolDb = AsyncSymbolDb (MVar (Either SomeException SymbolDb))++asyncLoadSymbolDb :: IOish m+ => MVar (Either SomeException SymbolDb)+ -> GhcModT m ()+asyncLoadSymbolDb mv = void $+ liftBaseWith $ \run -> forkIO $ void $ run $ do+ edb <- gtry loadSymbolDb+ liftIO $ putMVar mv edb++newAsyncSymbolDb :: IOish m => GhcModT m AsyncSymbolDb+newAsyncSymbolDb = do+ mv <- liftIO newEmptyMVar+ asyncLoadSymbolDb mv+ return $ AsyncSymbolDb mv++getAsyncSymbolDb :: forall m. IOish m => AsyncSymbolDb -> GhcModT m SymbolDb+getAsyncSymbolDb (AsyncSymbolDb mv) = do+ db <- liftIO $ handleEx <$> takeMVar mv+ outdated <- isOutdated db+ if outdated+ then do+ asyncLoadSymbolDb mv+ liftIO $ handleEx <$> readMVar mv+ else do+ liftIO $ putMVar mv $ Right db+ return db+ where+ handleEx edb =+ case edb of+ Left ex -> throw ex+ Right db -> db
+ GhcMod/Exe/Flag.hs view
@@ -0,0 +1,9 @@+module GhcMod.Exe.Flag where++import qualified GhcMod.Gap as Gap+import GhcMod.Convert+import GhcMod.Monad++-- | Listing of GHC flags, same as @ghc@\'s @--show-options@ with @ghc >= 7.10@.+flags :: IOish m => GhcModT m String+flags = convert' Gap.ghcCmdOptions
+ GhcMod/Exe/Info.hs view
@@ -0,0 +1,79 @@+module GhcMod.Exe.Info (+ info+ , types+ ) where++import Data.Function (on)+import Data.List (sortBy)+import System.FilePath+import Exception (ghandle, SomeException(..))+import GHC (GhcMonad, SrcSpan)+import Prelude+import qualified GHC as G++import qualified GhcMod.Gap as Gap+import GhcMod.Convert+import GhcMod.Doc+import GhcMod.DynFlags+import GhcMod.Gap+import GhcMod.Logging+import GhcMod.Monad+import GhcMod.SrcUtils+import GhcMod.Types+import GhcMod.Utils (mkRevRedirMapFunc)+import GhcMod.FileMapping (fileModSummaryWithMapping)++----------------------------------------------------------------++-- | Obtaining information of a target expression. (GHCi's info:)+info :: IOish m+ => FilePath -- ^ A target file.+ -> Expression -- ^ A Haskell expression.+ -> GhcModT m String+info file expr =+ ghandle handler $+ runGmlT' [Left file] deferErrors $+ withInteractiveContext $ do+ convert' =<< body+ where+ handler (SomeException ex) = do+ gmLog GmException "info" $ text "" $$ nest 4 (showToDoc ex)+ convert' "Cannot show info"++ body :: (GhcMonad m, GmState m, GmEnv m) => m String+ body = do+ m <- mkRevRedirMapFunc+ sdoc <- Gap.infoThing m expr+ st <- getStyle+ dflag <- G.getSessionDynFlags+ return $ showPage dflag st sdoc++----------------------------------------------------------------++-- | Obtaining type of a target expression. (GHCi's type:)+types :: IOish m+ => Bool -- ^ Include constraints into type signature+ -> FilePath -- ^ A target file.+ -> Int -- ^ Line number.+ -> Int -- ^ Column number.+ -> GhcModT m String+types withConstraints file lineNo colNo =+ ghandle handler $+ runGmlT' [Left file] deferErrors $+ withInteractiveContext $ do+ crdl <- cradle+ modSum <- fileModSummaryWithMapping (cradleCurrentDir crdl </> file)+ srcSpanTypes <- getSrcSpanType withConstraints modSum lineNo colNo+ dflag <- G.getSessionDynFlags+ st <- getStyle+ convert' $ map (toTup dflag st) $ sortBy (cmp `on` fst) srcSpanTypes+ where+ handler (SomeException ex) = do+ gmLog GmException "types" $ showToDoc ex+ return []++getSrcSpanType :: (GhcMonad m) => Bool -> G.ModSummary -> Int -> Int -> m [(SrcSpan, G.Type)]+getSrcSpanType withConstraints modSum lineNo colNo =+ G.parseModule modSum+ >>= G.typecheckModule+ >>= flip (collectSpansTypes withConstraints) (lineNo, colNo)
+ GhcMod/Exe/Internal.hs view
@@ -0,0 +1,73 @@+-- | Low level access to the ghc-mod library.++module GhcMod.Exe.Internal (+ -- * Types+ GHCOption+ , IncludeDir+ , GmlT(..)+ , MonadIO(..)+ , GmEnv(..)+ -- * Various Paths+ , ghcLibDir+ , ghcModExecutable+ -- * Logging+ , withLogger+ , setNoWarningFlags+ , setAllWarningFlags+ -- * Environment, state and logging+ , GhcModEnv(..)+ , GhcModState+ , GhcModLog+ , GmLog(..)+ , GmLogLevel(..)+ , gmSetLogLevel+ -- * Monad utilities+ , runGhcModT'+ , hoistGhcModT+ , runGmlT+ , runGmlT'+ , gmlGetSession+ , gmlSetSession+ , loadTargets+ , cabalResolvedComponents+ -- ** Accessing 'GhcModEnv' and 'GhcModState'+ , options+ , cradle+ , targetGhcOptions+ , withOptions+ -- * 'GhcModError'+ , gmeDoc+ -- * World+ , World+ , getCurrentWorld+ , didWorldChange+ -- * Cabal Helper+ , ModulePath(..)+ , GmComponent(..)+ , GmComponentType(..)+ , GmModuleGraph(..)+ , prepareCabalHelper+ -- * Misc stuff+ , GHandler(..)+ , gcatches+ -- * FileMapping+ , module GhcMod.FileMapping+ ) where++import GHC.Paths (libdir)++import GhcMod.Target+import GhcMod.DynFlags+import GhcMod.Error+import GhcMod.Logger+import GhcMod.Logging+import GhcMod.Monad+import GhcMod.Types+import GhcMod.Utils+import GhcMod.World+import GhcMod.CabalHelper+import GhcMod.FileMapping++-- | Obtaining the directory for ghc system libraries.+ghcLibDir :: FilePath+ghcLibDir = libdir
+ GhcMod/Exe/Lang.hs view
@@ -0,0 +1,10 @@+module GhcMod.Exe.Lang where++import DynFlags (supportedLanguagesAndExtensions)+import GhcMod.Convert+import GhcMod.Monad++-- | Listing language extensions.++languages :: IOish m => GhcModT m String+languages = convert' supportedLanguagesAndExtensions
+ GhcMod/Exe/Lint.hs view
@@ -0,0 +1,30 @@+module GhcMod.Exe.Lint where++import Exception (ghandle)+import Control.Exception (SomeException(..))+import GhcMod.Logger (checkErrorPrefix)+import GhcMod.Convert+import GhcMod.Types+import GhcMod.Monad+import Language.Haskell.HLint3++import GhcMod.Utils (withMappedFile)+import Language.Haskell.Exts.SrcLoc (SrcSpan(..))++-- | Checking syntax of a target file using hlint.+-- Warnings and errors are returned.+lint :: IOish m+ => LintOpts -- ^ Configuration parameters+ -> FilePath -- ^ A target file.+ -> GhcModT m String+lint opt file = ghandle handler $+ withMappedFile file $ \tempfile -> do+ res <- liftIO $ hlint $ "--quiet" : tempfile : optLintHlintOpts opt+ pack . map (show . substFile file tempfile) $ res+ where+ pack = convert' . map init -- init drops the last \n.+ handler (SomeException e) = return $ checkErrorPrefix ++ show e ++ "\n"+ substFile orig temp idea+ | srcSpanFilename (ideaSpan idea) == temp+ = idea{ideaSpan=(ideaSpan idea){srcSpanFilename = orig}}+ substFile _ _ idea = idea
+ GhcMod/Exe/Modules.hs view
@@ -0,0 +1,27 @@+module GhcMod.Exe.Modules (modules) where++import Control.Arrow+import Data.List+import GhcMod.Convert+import GhcMod.Types+import GhcMod.Monad+import GhcMod.Gap ( listVisibleModuleNames+ , lookupModulePackageInAllPackages+ )++import qualified GHC as G++----------------------------------------------------------------++-- | Listing installed modules.+modules :: (IOish m, Gm m)+ => Bool -- ^ 'detailed', if 'True', also prints packages that modules belong to.+ -> m String+modules detailed = do+ df <- runGmPkgGhc G.getSessionDynFlags+ let mns = listVisibleModuleNames df+ pmnss = map (first moduleNameString) $ zip mns (modulePkg df `map` mns)+ convert' $ nub [ if detailed then pkg ++ " " ++ mn else mn+ | (mn, pkgs) <- pmnss, pkg <- pkgs ]+ where+ modulePkg df = lookupModulePackageInAllPackages df
+ GhcMod/Exe/PkgDoc.hs view
@@ -0,0 +1,29 @@+module GhcMod.Exe.PkgDoc (pkgDoc) where++import GhcMod.Types+import GhcMod.GhcPkg+import GhcMod.Monad+import GhcMod.Output++import Control.Applicative+import Prelude++-- | Obtaining the package name and the doc path of a module.+pkgDoc :: IOish m => String -> GhcModT m String+pkgDoc mdl = do+ ghcPkg <- getGhcPkgProgram+ readProc <- gmReadProcess+ pkgDbStack <- getPackageDbStack+ pkg <- liftIO $ trim <$> readProc ghcPkg (toModuleOpts pkgDbStack) ""+ if pkg == "" then+ return "\n"+ else do+ htmlpath <- liftIO $ readProc ghcPkg (toDocDirOpts pkg pkgDbStack) ""+ let ret = pkg ++ " " ++ drop 14 htmlpath+ return ret+ where+ toModuleOpts dbs = ["find-module", mdl, "--simple-output"]+ ++ ghcPkgDbStackOpts dbs+ toDocDirOpts pkg dbs = ["field", pkg, "haddock-html"]+ ++ ghcPkgDbStackOpts dbs+ trim = takeWhile (`notElem` " \n")
+ GhcMod/Exe/Test.hs view
@@ -0,0 +1,45 @@+module GhcMod.Exe.Test where++import Control.Applicative+import Data.List+import System.FilePath+import System.Directory+import Prelude++import GhcMod.Types+import GhcMod.Monad+import GhcMod.DynFlags++import GHC+import GHC.Exception+import OccName++test :: IOish m+ => FilePath -> GhcModT m String+test f = runGmlT' [Left f] (fmap setHscInterpreted . deferErrors) $ do+ mg <- getModuleGraph+ root <- cradleRootDir <$> cradle+ f' <- makeRelative root <$> liftIO (canonicalizePath f)+ let Just ms = find ((==Just f') . ml_hs_file . ms_location) mg+ mdl = ms_mod ms+ mn = moduleName mdl++ Just mi <- getModuleInfo mdl+ let exs = map (occNameString . getOccName) $ modInfoExports mi+ cqs = filter ("prop_" `isPrefixOf`) exs++ setContext [ IIDecl $ simpleImportDecl mn+ , IIDecl $ simpleImportDecl $ mkModuleName "Test.QuickCheck"+ ]++ _res <- mapM runTest cqs++ return ""++runTest :: GhcMonad m => String -> m (Maybe SomeException)+runTest fn = do+ res <- runStmt ("quickCheck " ++ fn) RunToCompletion+ return $ case res of+ RunOk [] -> Nothing+ RunException se -> Just se+ _ -> error "runTest"
− LICENSE
@@ -1,29 +0,0 @@-Copyright (c) 2009, IIJ Innovation Institute Inc.-All rights reserved.--Redistribution and use in source and binary forms, with or without-modification, are permitted provided that the following conditions-are met:-- * Redistributions of source code must retain the above copyright- notice, this list of conditions and the following disclaimer.- * Redistributions in binary form must reproduce the above copyright- notice, this list of conditions and the following disclaimer in- the documentation and/or other materials provided with the- distribution.- * Neither the name of the copyright holders nor the names of its- contributors may be used to endorse or promote products derived- from this software without specific prior written permission.--THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS-FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE-COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,-BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;-LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER-CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN-ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE-POSSIBILITY OF SUCH DAMAGE.
− Language/Haskell/GhcMod.hs
@@ -1,66 +0,0 @@--- | The ghc-mod library.--module Language.Haskell.GhcMod (- -- * Cradle- Cradle(..)- , findCradle- -- * Options- , Options(..)- , LineSeparator(..)- , OutputStyle(..)- , defaultOptions- -- * Types- , ModuleString- , Expression- , GhcPkgDb- , Symbol- , SymbolDb- , GhcModError(..)- -- * Monad Types- , GhcModT- , IOish- -- * Monad utilities- , runGhcModT- , withOptions- -- * 'GhcMod' utilities- , boot- , browse- , check- , checkSyntax- , debugInfo- , expandTemplate- , info- , lint- , pkgDoc- , rootInfo- , types- , splits- , sig- , refine- , auto- , modules- , languages- , flags- , findSymbol- , lookupSymbol- , dumpSymbol- -- * SymbolDb- , loadSymbolDb- ) where--import Language.Haskell.GhcMod.Boot-import Language.Haskell.GhcMod.Browse-import Language.Haskell.GhcMod.CaseSplit-import Language.Haskell.GhcMod.Check-import Language.Haskell.GhcMod.Cradle-import Language.Haskell.GhcMod.Debug-import Language.Haskell.GhcMod.FillSig-import Language.Haskell.GhcMod.Find-import Language.Haskell.GhcMod.Flag-import Language.Haskell.GhcMod.Info-import Language.Haskell.GhcMod.Lang-import Language.Haskell.GhcMod.Lint-import Language.Haskell.GhcMod.Monad-import Language.Haskell.GhcMod.Modules-import Language.Haskell.GhcMod.PkgDoc-import Language.Haskell.GhcMod.Types
− Language/Haskell/GhcMod/Boot.hs
@@ -1,25 +0,0 @@-module Language.Haskell.GhcMod.Boot where--import Control.Applicative-import Language.Haskell.GhcMod.Browse-import Language.Haskell.GhcMod.Flag-import Language.Haskell.GhcMod.Lang-import Language.Haskell.GhcMod.Monad-import Language.Haskell.GhcMod.Modules---- | Printing necessary information for front-end booting.-boot :: IOish m => GhcModT m String-boot = concat <$> sequence [modules, languages, flags,- concat <$> mapM browse preBrowsedModules]--preBrowsedModules :: [String]-preBrowsedModules = [- "Prelude"- , "Control.Applicative"- , "Control.Exception"- , "Control.Monad"- , "Data.Char"- , "Data.List"- , "Data.Maybe"- , "System.IO"- ]
− Language/Haskell/GhcMod/Browse.hs
@@ -1,136 +0,0 @@-module Language.Haskell.GhcMod.Browse (- browse- ) where--import Control.Applicative ((<$>))-import Control.Exception (SomeException(..))-import Data.Char (isAlpha)-import Data.List (sort)-import Data.Maybe (catMaybes)-import Exception (ghandle)-import FastString (mkFastString)-import GHC (GhcException(CmdLineError), ModuleInfo, Name, TyThing, DynFlags, Type, TyCon)-import qualified GHC as G-import Language.Haskell.GhcMod.Convert-import Language.Haskell.GhcMod.Doc (showPage, styleUnqualified)-import Language.Haskell.GhcMod.Gap-import Language.Haskell.GhcMod.Monad (GhcModT, options)-import Language.Haskell.GhcMod.Target (setTargetFiles)-import Language.Haskell.GhcMod.Types-import Name (getOccString)-import Outputable (ppr, Outputable)-import TyCon (isAlgTyCon)-import Type (dropForAlls, splitFunTy_maybe, mkFunTy, isPredTy)---------------------------------------------------------------------- | Getting functions, classes, etc from a module.--- If 'detailed' is 'True', their types are also obtained.--- If 'operators' is 'True', operators are also returned.-browse :: IOish m- => ModuleString -- ^ A module name. (e.g. \"Data.List\")- -> GhcModT m String-browse pkgmdl = convert' . sort =<< (listExports =<< getModule)- where- (mpkg,mdl) = splitPkgMdl pkgmdl- mdlname = G.mkModuleName mdl- mpkgid = mkFastString <$> mpkg- listExports Nothing = return []- listExports (Just mdinfo) = processExports mdinfo- -- findModule works only for package modules, moreover,- -- you cannot load a package module. On the other hand,- -- to browse a local module you need to load it first.- -- If CmdLineError is signalled, we assume the user- -- tried browsing a local module.- getModule = browsePackageModule `G.gcatch` fallback `G.gcatch` handler- browsePackageModule = G.findModule mdlname mpkgid >>= G.getModuleInfo- browseLocalModule = ghandle handler $ do- setTargetFiles [mdl]- G.findModule mdlname Nothing >>= G.getModuleInfo- fallback (CmdLineError _) = browseLocalModule- fallback _ = return Nothing- handler (SomeException _) = return Nothing--- |------ >>> splitPkgMdl "base:Prelude"--- (Just "base","Prelude")--- >>> splitPkgMdl "Prelude"--- (Nothing,"Prelude")-splitPkgMdl :: String -> (Maybe String,String)-splitPkgMdl pkgmdl = case break (==':') pkgmdl of- (mdl,"") -> (Nothing,mdl)- (pkg,_:mdl) -> (Just pkg,mdl)--processExports :: IOish m => ModuleInfo -> GhcModT m [String]-processExports minfo = do- opt <- options- let- removeOps- | operators opt = id- | otherwise = filter (isAlpha . head . getOccString)- mapM (showExport opt minfo) $ removeOps $ G.modInfoExports minfo--showExport :: IOish m => Options -> ModuleInfo -> Name -> GhcModT m String-showExport opt minfo e = do- mtype' <- mtype- return $ concat $ catMaybes [mqualified, Just $ formatOp $ getOccString e, mtype']- where- mqualified = (G.moduleNameString (G.moduleName $ G.nameModule e) ++ ".") `justIf` qualified opt- mtype :: IOish m => GhcModT m (Maybe String)- mtype- | detailed opt = do- tyInfo <- G.modInfoLookupName minfo e- -- If nothing found, load dependent module and lookup global- tyResult <- maybe (inOtherModule e) (return . Just) tyInfo- dflag <- G.getSessionDynFlags- return $ do- typeName <- tyResult >>= showThing dflag- (" :: " ++ typeName) `justIf` detailed opt- | otherwise = return Nothing- formatOp nm@(n:_)- | isAlpha n = nm- | otherwise = "(" ++ nm ++ ")"- formatOp "" = error "formatOp"- inOtherModule :: IOish m => Name -> GhcModT m (Maybe TyThing)- inOtherModule nm = G.getModuleInfo (G.nameModule nm) >> G.lookupGlobalName nm- justIf :: a -> Bool -> Maybe a- justIf x True = Just x- justIf _ False = Nothing--showThing :: DynFlags -> TyThing -> Maybe String-showThing dflag tything = showThing' dflag (fromTyThing tything)--showThing' :: DynFlags -> GapThing -> Maybe String-showThing' dflag (GtA a) = Just $ formatType dflag a-showThing' _ (GtT t) = unwords . toList <$> tyType t- where- toList t' = t' : getOccString t : map getOccString (G.tyConTyVars t)-showThing' _ _ = Nothing--formatType :: DynFlags -> Type -> String-formatType dflag a = showOutputable dflag (removeForAlls a)--tyType :: TyCon -> Maybe String-tyType typ- | isAlgTyCon typ- && not (G.isNewTyCon typ)- && not (G.isClassTyCon typ) = Just "data"- | G.isNewTyCon typ = Just "newtype"- | G.isClassTyCon typ = Just "class"- | G.isSynTyCon typ = Just "type"- | otherwise = Nothing--removeForAlls :: Type -> Type-removeForAlls ty = removeForAlls' ty' tty'- where- ty' = dropForAlls ty- tty' = splitFunTy_maybe ty'--removeForAlls' :: Type -> Maybe (Type, Type) -> Type-removeForAlls' ty Nothing = ty-removeForAlls' ty (Just (pre, ftype))- | isPredTy pre = mkFunTy pre (dropForAlls ftype)- | otherwise = ty--showOutputable :: Outputable a => DynFlags -> a -> String-showOutputable dflag = unwords . lines . showPage dflag styleUnqualified . ppr
− Language/Haskell/GhcMod/Cabal16.hs
@@ -1,45 +0,0 @@--- Copyright : Isaac Jones 2003-2004-{- All rights reserved.--Redistribution and use in source and binary forms, with or without-modification, are permitted provided that the following conditions are-met:-- * Redistributions of source code must retain the above copyright- notice, this list of conditions and the following disclaimer.-- * Redistributions in binary form must reproduce the above- copyright notice, this list of conditions and the following- disclaimer in the documentation and/or other materials provided- with the distribution.-- * Neither the name of Isaac Jones nor the names of other- contributors may be used to endorse or promote products derived- from this software without specific prior written permission.--THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -}----- | ComponentLocalBuildInfo for Cabal <= 1.16-module Language.Haskell.GhcMod.Cabal16 (- ComponentLocalBuildInfo- , componentPackageDeps- ) where--import Distribution.Package (InstalledPackageId, PackageIdentifier)---- From Cabal <= 1.16-data ComponentLocalBuildInfo = ComponentLocalBuildInfo {- componentPackageDeps :: [(InstalledPackageId, PackageIdentifier)]- }- deriving (Read, Show)
− Language/Haskell/GhcMod/Cabal18.hs
@@ -1,58 +0,0 @@--- Copyright : Isaac Jones 2003-2004-{- All rights reserved.--Redistribution and use in source and binary forms, with or without-modification, are permitted provided that the following conditions are-met:-- * Redistributions of source code must retain the above copyright- notice, this list of conditions and the following disclaimer.-- * Redistributions in binary form must reproduce the above- copyright notice, this list of conditions and the following- disclaimer in the documentation and/or other materials provided- with the distribution.-- * Neither the name of Isaac Jones nor the names of other- contributors may be used to endorse or promote products derived- from this software without specific prior written permission.--THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -}---- | ComponentLocalBuildInfo for Cabal >= 1.18-module Language.Haskell.GhcMod.Cabal18 (- ComponentLocalBuildInfo- , componentPackageDeps- , componentLibraries- ) where--import Distribution.Package (InstalledPackageId, PackageId)--data LibraryName = LibraryName String- deriving (Read, Show)--data ComponentLocalBuildInfo- = LibComponentLocalBuildInfo {- componentPackageDeps :: [(InstalledPackageId, PackageId)],- componentLibraries :: [LibraryName]- }- | ExeComponentLocalBuildInfo {- componentPackageDeps :: [(InstalledPackageId, PackageId)]- }- | TestComponentLocalBuildInfo {- componentPackageDeps :: [(InstalledPackageId, PackageId)]- }- | BenchComponentLocalBuildInfo {- componentPackageDeps :: [(InstalledPackageId, PackageId)]- }- deriving (Read, Show)
− Language/Haskell/GhcMod/Cabal21.hs
@@ -1,73 +0,0 @@--- Copyright : Isaac Jones 2003-2004-{- All rights reserved.--Redistribution and use in source and binary forms, with or without-modification, are permitted provided that the following conditions are-met:-- * Redistributions of source code must retain the above copyright- notice, this list of conditions and the following disclaimer.-- * Redistributions in binary form must reproduce the above- copyright notice, this list of conditions and the following- disclaimer in the documentation and/or other materials provided- with the distribution.-- * Neither the name of Isaac Jones nor the names of other- contributors may be used to endorse or promote products derived- from this software without specific prior written permission.--THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -}---- | ComponentLocalBuildInfo for Cabal >= 1.21-module Language.Haskell.GhcMod.Cabal21 (- ComponentLocalBuildInfo- , PackageIdentifier(..)- , PackageName(..)- , componentPackageDeps- , componentLibraries- ) where--import Distribution.Package (InstalledPackageId)-import Data.Version (Version)--data LibraryName = LibraryName String- deriving (Read, Show)--newtype PackageName = PackageName { unPackageName :: String }- deriving (Read, Show)--data PackageIdentifier- = PackageIdentifier {- pkgName :: PackageName,- pkgVersion :: Version- }- deriving (Read, Show)--type PackageId = PackageIdentifier--data ComponentLocalBuildInfo- = LibComponentLocalBuildInfo {- componentPackageDeps :: [(InstalledPackageId, PackageId)],- componentLibraries :: [LibraryName]- }- | ExeComponentLocalBuildInfo {- componentPackageDeps :: [(InstalledPackageId, PackageId)]- }- | TestComponentLocalBuildInfo {- componentPackageDeps :: [(InstalledPackageId, PackageId)]- }- | BenchComponentLocalBuildInfo {- componentPackageDeps :: [(InstalledPackageId, PackageId)]- }- deriving (Read, Show)
− Language/Haskell/GhcMod/CabalApi.hs
@@ -1,193 +0,0 @@-{-# LANGUAGE OverloadedStrings, CPP #-}--module Language.Haskell.GhcMod.CabalApi (- getCompilerOptions- , parseCabalFile- , cabalAllBuildInfo- , cabalDependPackages- , cabalSourceDirs- , cabalAllTargets- , cabalConfigDependencies- ) where--import Language.Haskell.GhcMod.CabalConfig-import Language.Haskell.GhcMod.Error-import Language.Haskell.GhcMod.Gap (benchmarkBuildInfo, benchmarkTargets,- toModuleString)-import Language.Haskell.GhcMod.GhcPkg-import Language.Haskell.GhcMod.Types--import MonadUtils (liftIO)-import Control.Applicative ((<$>))-import qualified Control.Exception as E-import Control.Monad (filterM)-import Data.Maybe (maybeToList)-import Data.Set (fromList, toList)-import Distribution.Package (Dependency(Dependency)- , PackageName(PackageName))-import qualified Distribution.Package as C-import Distribution.PackageDescription (PackageDescription, BuildInfo, TestSuite, TestSuiteInterface(..), Executable)-import qualified Distribution.PackageDescription as P-import Distribution.PackageDescription.Configuration (finalizePackageDescription)-import Distribution.PackageDescription.Parse (readPackageDescription)-import Distribution.Simple.Compiler (CompilerId(..), CompilerFlavor(..))-import Distribution.Simple.Program (ghcProgram)-import Distribution.Simple.Program.Types (programName, programFindVersion)-import Distribution.System (buildPlatform)-import Distribution.Text (display)-import Distribution.Verbosity (silent)-import Distribution.Version (Version)-import System.Directory (doesFileExist)-import System.FilePath ((</>))--------------------------------------------------------------------- | Getting necessary 'CompilerOptions' from three information sources.-getCompilerOptions :: (IOish m, MonadError GhcModError m)- => [GHCOption]- -> Cradle- -> PackageDescription- -> m CompilerOptions-getCompilerOptions ghcopts cradle pkgDesc = do- gopts <- liftIO $ getGHCOptions ghcopts cradle rdir $ head buildInfos- depPkgs <- cabalConfigDependencies cradle (C.packageId pkgDesc)- return $ CompilerOptions gopts idirs depPkgs- where- wdir = cradleCurrentDir cradle- rdir = cradleRootDir cradle- buildInfos = cabalAllBuildInfo pkgDesc- idirs = includeDirectories rdir wdir $ cabalSourceDirs buildInfos--------------------------------------------------------------------- Include directories for modules--cabalBuildDirs :: [FilePath]-cabalBuildDirs = ["dist/build", "dist/build/autogen"]--includeDirectories :: FilePath -> FilePath -> [FilePath] -> [FilePath]-includeDirectories cdir wdir dirs = uniqueAndSort (extdirs ++ [cdir,wdir])- where- extdirs = map expand $ dirs ++ cabalBuildDirs- expand "." = cdir- expand subdir = cdir </> subdir---------------------------------------------------------------------- | Parse a cabal file and return a 'PackageDescription'.-parseCabalFile :: (IOish m, MonadError GhcModError m)- => Cradle- -> FilePath- -> m PackageDescription-parseCabalFile cradle file = do- cid <- liftIO getGHCId- epgd <- liftIO $ readPackageDescription silent file- flags <- cabalConfigFlags cradle- case toPkgDesc cid flags epgd of- Left deps -> fail $ show deps ++ " are not installed"- Right (pd,_) -> if nullPkg pd- then fail $ file ++ " is broken"- else return pd- where- toPkgDesc cid flags =- finalizePackageDescription flags (const True) buildPlatform cid []- nullPkg pd = name == ""- where- PackageName name = C.pkgName (P.package pd)--------------------------------------------------------------------getGHCOptions :: [GHCOption] -> Cradle -> FilePath -> BuildInfo -> IO [GHCOption]-getGHCOptions ghcopts cradle rdir binfo = do- cabalCpp <- cabalCppOptions rdir- let cpps = map ("-optP" ++) $ P.cppOptions binfo ++ cabalCpp- return $ ghcopts ++ pkgDb ++ exts ++ [lang] ++ libs ++ libDirs ++ cpps- where- pkgDb = ghcDbStackOpts $ cradlePkgDbStack cradle- lang = maybe "-XHaskell98" (("-X" ++) . display) $ P.defaultLanguage binfo- libDirs = map ("-L" ++) $ P.extraLibDirs binfo- exts = map (("-X" ++) . display) $ P.usedExtensions binfo- libs = map ("-l" ++) $ P.extraLibs binfo--cabalCppOptions :: FilePath -> IO [String]-cabalCppOptions dir = do- exist <- doesFileExist cabalMacro- return $ if exist then- ["-include", cabalMacro]- else- []- where- cabalMacro = dir </> "dist/build/autogen/cabal_macros.h"---------------------------------------------------------------------- | Extracting all 'BuildInfo' for libraries, executables, and tests.-cabalAllBuildInfo :: PackageDescription -> [BuildInfo]-cabalAllBuildInfo pd = libBI ++ execBI ++ testBI ++ benchBI- where- libBI = map P.libBuildInfo $ maybeToList $ P.library pd- execBI = map P.buildInfo $ P.executables pd- testBI = map P.testBuildInfo $ P.testSuites pd- benchBI = benchmarkBuildInfo pd---------------------------------------------------------------------- | Extracting package names of dependency.-cabalDependPackages :: [BuildInfo] -> [PackageBaseName]-cabalDependPackages bis = uniqueAndSort pkgs- where- pkgs = map getDependencyPackageName $ concatMap P.targetBuildDepends bis- getDependencyPackageName (Dependency (PackageName nm) _) = nm---------------------------------------------------------------------- | Extracting include directories for modules.-cabalSourceDirs :: [BuildInfo] -> [IncludeDir]-cabalSourceDirs bis = uniqueAndSort $ concatMap P.hsSourceDirs bis--------------------------------------------------------------------uniqueAndSort :: [String] -> [String]-uniqueAndSort = toList . fromList--------------------------------------------------------------------getGHCId :: IO CompilerId-getGHCId = CompilerId GHC <$> getGHC--getGHC :: IO Version-getGHC = do- mv <- programFindVersion ghcProgram silent (programName ghcProgram)- case mv of- -- TODO: MonadError it up- Nothing -> E.throwIO $ userError "ghc not found"- Just v -> return v---------------------------------------------------------------------- | Extracting all 'Module' 'FilePath's for libraries, executables,--- tests and benchmarks.-cabalAllTargets :: PackageDescription -> IO ([String],[String],[String],[String])-cabalAllTargets pd = do- exeTargets <- mapM getExecutableTarget $ P.executables pd- testTargets <- mapM getTestTarget $ P.testSuites pd- return (libTargets,concat exeTargets,concat testTargets,benchTargets)- where- lib = case P.library pd of- Nothing -> []- Just l -> P.libModules l-- libTargets = map toModuleString lib- benchTargets = benchmarkTargets pd-- getTestTarget :: TestSuite -> IO [String]- getTestTarget ts =- case P.testInterface ts of- (TestSuiteExeV10 _ filePath) -> do- let maybeTests = [p </> e | p <- P.hsSourceDirs $ P.testBuildInfo ts, e <- [filePath]]- liftIO $ filterM doesFileExist maybeTests- (TestSuiteLibV09 _ moduleName) -> return [toModuleString moduleName]- (TestSuiteUnsupported _) -> return []-- getExecutableTarget :: Executable -> IO [String]- getExecutableTarget exe = do- let maybeExes = [p </> e | p <- P.hsSourceDirs $ P.buildInfo exe, e <- [P.modulePath exe]]- liftIO $ filterM doesFileExist maybeExes
− Language/Haskell/GhcMod/CabalConfig.hs
@@ -1,177 +0,0 @@-{-# LANGUAGE CPP #-}---- | This module facilitates extracting information from Cabal's on-disk--- 'LocalBuildInfo' (@dist/setup-config@).-module Language.Haskell.GhcMod.CabalConfig (- CabalConfig- , cabalConfigDependencies- , cabalConfigFlags- ) where--import Language.Haskell.GhcMod.Error-import Language.Haskell.GhcMod.GhcPkg-import Language.Haskell.GhcMod.Utils-import Language.Haskell.GhcMod.Read-import Language.Haskell.GhcMod.Types--import qualified Language.Haskell.GhcMod.Cabal16 as C16-import qualified Language.Haskell.GhcMod.Cabal18 as C18-import qualified Language.Haskell.GhcMod.Cabal21 as C21--#ifndef MIN_VERSION_mtl-#define MIN_VERSION_mtl(x,y,z) 1-#endif--import Control.Applicative ((<$>))-import Control.Monad (mplus)-#if MIN_VERSION_mtl(2,2,1)-import Control.Monad.Except ()-#else-import Control.Monad.Error ()-#endif-import Data.Maybe ()-import Data.Set ()-import Data.List (find,tails,isPrefixOf,isInfixOf,nub,stripPrefix)-import Distribution.Package (InstalledPackageId(..)- , PackageIdentifier(..)- , PackageName(..))-import Distribution.PackageDescription (FlagAssignment)-import Distribution.Simple.BuildPaths (defaultDistPref)-import Distribution.Simple.Configure (localBuildInfoFile)-import Distribution.Simple.LocalBuildInfo (ComponentName)-import MonadUtils (liftIO)-import System.FilePath ((</>))---------------------------------------------------------------------- | 'Show'ed cabal 'LocalBuildInfo' string-type CabalConfig = String---- | Get contents of the file containing 'LocalBuildInfo' data. If it doesn't--- exist run @cabal configure@ i.e. configure with default options like @cabal--- build@ would do.-getConfig :: (IOish m, MonadError GhcModError m)- => Cradle- -> m CabalConfig-getConfig cradle = liftIO (readFile path) `tryFix` \_ ->- configure `modifyError'` GMECabalConfigure- where- prjDir = cradleRootDir cradle- path = prjDir </> configPath-- configure :: (IOish m, MonadError GhcModError m) => m ()- configure =- withDirectory_ prjDir $ readProcess' "cabal" ["configure"] >> return ()----- | Path to 'LocalBuildInfo' file, usually @dist/setup-config@-configPath :: FilePath-configPath = localBuildInfoFile defaultDistPref---- | Get list of 'Package's needed by all components of the current package-cabalConfigDependencies :: (IOish m, MonadError GhcModError m)- => Cradle- -> PackageIdentifier- -> m [Package]-cabalConfigDependencies cradle thisPkg =- configDependencies thisPkg <$> getConfig cradle---- | Extract list of depencenies for all components from 'CabalConfig'-configDependencies :: PackageIdentifier -> CabalConfig -> [Package]-configDependencies thisPkg config = map fromInstalledPackageId deps- where- deps :: [InstalledPackageId]- deps = case deps21 `mplus` deps18 `mplus` deps16 of- Right ps -> ps- Left msg -> error msg-- -- True if this dependency is an internal one (depends on the library- -- defined in the same package).- internal pkgid = pkgid == thisPkg-- -- Cabal >= 1.21- deps21 :: Either String [InstalledPackageId]- deps21 =- map fst- <$> filterInternal21- <$> (readEither =<< extractField config "componentsConfigs")-- filterInternal21- :: [(ComponentName, C21.ComponentLocalBuildInfo, [ComponentName])]- -> [(InstalledPackageId, C21.PackageIdentifier)]-- filterInternal21 ccfg = [ (ipkgid, pkgid)- | (_,clbi,_) <- ccfg- , (ipkgid, pkgid) <- C21.componentPackageDeps clbi- , not (internal . packageIdentifierFrom21 $ pkgid) ]-- packageIdentifierFrom21 :: C21.PackageIdentifier -> PackageIdentifier- packageIdentifierFrom21 (C21.PackageIdentifier (C21.PackageName myName) myVersion) =- PackageIdentifier (PackageName myName) myVersion-- -- Cabal >= 1.18 && < 1.21- deps18 :: Either String [InstalledPackageId]- deps18 =- map fst- <$> filterInternal- <$> (readEither =<< extractField config "componentsConfigs")-- filterInternal- :: [(ComponentName, C18.ComponentLocalBuildInfo, [ComponentName])]- -> [(InstalledPackageId, PackageIdentifier)]-- filterInternal ccfg = [ (ipkgid, pkgid)- | (_,clbi,_) <- ccfg- , (ipkgid, pkgid) <- C18.componentPackageDeps clbi- , not (internal pkgid) ]-- -- Cabal 1.16 and below- deps16 :: Either String [InstalledPackageId]- deps16 = map fst <$> filter (not . internal . snd) . nub <$> do- cbi <- concat <$> sequence [ extract "executableConfigs"- , extract "testSuiteConfigs"- , extract "benchmarkConfigs" ]- :: Either String [(String, C16.ComponentLocalBuildInfo)]-- return $ maybe [] C16.componentPackageDeps libraryConfig- ++ concatMap (C16.componentPackageDeps . snd) cbi- where- libraryConfig :: Maybe C16.ComponentLocalBuildInfo- libraryConfig = do- field <- find ("libraryConfig" `isPrefixOf`) (tails config)- clbi <- stripPrefix " = " field- if "Nothing" `isPrefixOf` clbi- then Nothing- else case readMaybe =<< stripPrefix "Just " clbi of- Just x -> x- Nothing -> error $ "reading libraryConfig failed\n" ++ show (stripPrefix "Just " clbi)-- extract :: String -> Either String [(String, C16.ComponentLocalBuildInfo)]- extract field = readConfigs field <$> extractField config field-- readConfigs :: String -> String -> [(String, C16.ComponentLocalBuildInfo)]- readConfigs f s = case readEither s of- Right x -> x- Left msg -> error $ "reading config " ++ f ++ " failed ("++msg++")"---- | Get the flag assignment from the local build info of the given cradle-cabalConfigFlags :: (IOish m, MonadError GhcModError m)- => Cradle- -> m FlagAssignment-cabalConfigFlags cradle = do- config <- getConfig cradle- case configFlags config of- Right x -> return x- Left msg -> throwError (GMECabalFlags (GMEString msg))---- | Extract the cabal flags from the 'CabalConfig'-configFlags :: CabalConfig -> Either String FlagAssignment-configFlags config = readEither =<< flip extractField "configConfigurationsFlags" =<< extractField config "configFlags"---- | Find @field@ in 'CabalConfig'. Returns 'Left' containing a user readable--- error message with lots of context on failure.-extractField :: CabalConfig -> String -> Either String String-extractField config field =- case extractParens <$> find (field `isPrefixOf`) (tails config) of- Just f -> Right f- Nothing -> Left $ "extractField: failed extracting "++field++" from input, input contained `"++field++"'? " ++ show (field `isInfixOf` config)
− Language/Haskell/GhcMod/CaseSplit.hs
@@ -1,222 +0,0 @@-{-# LANGUAGE CPP #-}--module Language.Haskell.GhcMod.CaseSplit (- splits- ) where--import Data.List (find, intercalate)-import Data.Maybe (isJust)-import qualified Data.Text as T-import qualified Data.Text.IO as T (readFile)-import qualified DataCon as Ty-import Exception (ghandle, SomeException(..))-import GHC (GhcMonad, LPat, Id, ParsedModule(..), TypecheckedModule(..), DynFlags, SrcSpan, Type, GenLocated(L))-import qualified GHC as G-import Language.Haskell.GhcMod.Convert-import qualified Language.Haskell.GhcMod.Gap as Gap-import Language.Haskell.GhcMod.Monad-import Language.Haskell.GhcMod.SrcUtils-import Outputable (PprStyle)-import qualified TyCon as Ty-import qualified Type as Ty--------------------------------------------------------------------- CASE SPLITTING-------------------------------------------------------------------data SplitInfo = SplitInfo G.Name SrcSpan (SrcSpan, Type) [SrcSpan]- | TySplitInfo G.Name SrcSpan (SrcSpan, Ty.Kind)-data SplitToTextInfo = SplitToTextInfo { sVarName :: String- , sBindingSpan :: SrcSpan- , sVarSpan :: SrcSpan- , sTycons :: [String]- }---- | Splitting a variable in a equation.-splits :: IOish m- => FilePath -- ^ A target file.- -> Int -- ^ Line number.- -> Int -- ^ Column number.- -> GhcModT m String-splits file lineNo colNo = ghandle handler body- where- body = inModuleContext file $ \dflag style -> do- opt <- options- modSum <- Gap.fileModSummary file- whenFound' opt (getSrcSpanTypeForSplit modSum lineNo colNo) $ \x -> case x of- (SplitInfo varName bndLoc (varLoc,varT) _matches) -> do- let varName' = showName dflag style varName -- Convert name to string- text <- genCaseSplitTextFile file (SplitToTextInfo varName' bndLoc varLoc $- getTyCons dflag style varName varT)- return (fourInts bndLoc, text)- (TySplitInfo varName bndLoc (varLoc,varT)) -> do- let varName' = showName dflag style varName -- Convert name to string- text <- genCaseSplitTextFile file (SplitToTextInfo varName' bndLoc varLoc $- getTyCons dflag style varName varT)- return (fourInts bndLoc, text)- handler (SomeException _) = emptyResult =<< options--------------------------------------------------------------------- a. Code for getting the information of the variable--getSrcSpanTypeForSplit :: GhcMonad m => G.ModSummary -> Int -> Int -> m (Maybe SplitInfo)-getSrcSpanTypeForSplit modSum lineNo colNo = do- fn <- getSrcSpanTypeForFnSplit modSum lineNo colNo- if isJust fn- then return fn- else getSrcSpanTypeForTypeSplit modSum lineNo colNo---- Information for a function case split-getSrcSpanTypeForFnSplit :: GhcMonad m => G.ModSummary -> Int -> Int -> m (Maybe SplitInfo)-getSrcSpanTypeForFnSplit modSum lineNo colNo = do- p@ParsedModule{pm_parsed_source = _pms} <- G.parseModule modSum- tcm@TypecheckedModule{tm_typechecked_source = tcs} <- G.typecheckModule p- let varPat = find isPatternVar $ listifySpans tcs (lineNo, colNo) :: Maybe (LPat Id)- match:_ = listifySpans tcs (lineNo, colNo) :: [Gap.GLMatchI]- case varPat of- Nothing -> return Nothing- Just varPat' -> do- varT <- Gap.getType tcm varPat' -- Finally we get the type of the var- case varT of- Just varT' ->- let (L matchL (G.Match _ _ (G.GRHSs rhsLs _))) = match- in return $ Just (SplitInfo (getPatternVarName varPat') matchL varT' (map G.getLoc rhsLs) )- _ -> return Nothing--isPatternVar :: LPat Id -> Bool-isPatternVar (L _ (G.VarPat _)) = True-isPatternVar _ = False--getPatternVarName :: LPat Id -> G.Name-getPatternVarName (L _ (G.VarPat vName)) = G.getName vName-getPatternVarName _ = error "This should never happened"---- TODO: Information for a type family case split-getSrcSpanTypeForTypeSplit :: GhcMonad m => G.ModSummary -> Int -> Int -> m (Maybe SplitInfo)-getSrcSpanTypeForTypeSplit _modSum _lineNo _colNo = return Nothing--------------------------------------------------------------------- b. Code for getting the possible constructors--getTyCons :: DynFlags -> PprStyle -> G.Name -> G.Type -> [String]-getTyCons dflag style name ty | Just (tyCon, _) <- Ty.splitTyConApp_maybe ty =- let name' = showName dflag style name -- Convert name to string- in getTyCon dflag style name' tyCon-getTyCons dflag style name _ = [showName dflag style name]---- Write cases for one type-getTyCon :: DynFlags -> PprStyle -> String -> Ty.TyCon -> [String]--- 1. Non-matcheable type constructors-getTyCon _ _ name tyCon | isNotMatcheableTyCon tyCon = [name]--- 2. Special cases--- 2.1. Tuples-getTyCon _ _ name tyCon | Ty.isTupleTyCon tyCon =- let [uniqueDataCon] = Ty.tyConDataCons tyCon- tupleArity = Ty.dataConSourceArity uniqueDataCon- -- Deal with both boxed and unboxed tuples- isUnboxed = Ty.isUnboxedTupleTyCon tyCon- startSign = if isUnboxed then "(#" else "("- endSign = if isUnboxed then "#)" else ")"- in [ startSign ++ intercalate "," (map (\n -> name ++ show n) [1 .. tupleArity]) ++ endSign ]--- 3. General case-getTyCon dflag style name tyCon = map (getDataCon dflag style name) (Ty.tyConDataCons tyCon)---- These type constructors should not be matched against-isNotMatcheableTyCon :: Ty.TyCon -> Bool-isNotMatcheableTyCon ty = Ty.isPrimTyCon ty -- Primitive types, such as Int#- || Ty.isFunTyCon ty -- Function types---- Write case for one constructor-getDataCon :: DynFlags -> PprStyle -> String -> Ty.DataCon -> String--- 1. Infix constructors-getDataCon dflag style vName dcon | Ty.dataConIsInfix dcon =- let dName = showName dflag style $ Ty.dataConName dcon- in case Ty.dataConSourceArity dcon of- 0 -> dName- 1 -> vName ++ dName- n -> if dName == ":" -- Special case for lists- then vName ++ ":" ++ vName ++ "s"- else newVar vName 1 ++ " " ++ dName ++ " " ++ newVars vName 2 (n-1)--- 2. Non-record, non-infix syntax-getDataCon dflag style vName dcon | [] <- Ty.dataConFieldLabels dcon =- let dName = showName dflag style $ Ty.dataConName dcon- in if last dName == '#' -- Special case for I#, C# and so on- then vName- else case Ty.dataConSourceArity dcon of- 0 -> dName- _ -> dName ++ " " ++ newVarsSpecialSingleton vName 1 (Ty.dataConSourceArity dcon)--- 3. Records-getDataCon dflag style vName dcon =- let dName = showName dflag style $ Ty.dataConName dcon- flds = Ty.dataConFieldLabels dcon- in dName ++ " { " ++ showFieldNames dflag style vName flds ++ " }"---- Create a new variable by adjoining a number-newVar :: String -> Int -> String-newVar v n = v ++ show n---- Create a list of variables which start with the same prefix-newVars :: String -> Int -> Int -> String-newVars _ _ 0 = ""-newVars v s 1 = newVar v s-newVars v s m = newVar v s ++ " " ++ newVars v (s+1) (m-1)---- Create a list of variables which start with the same prefix--- Special case for a single variable, in which case no number is adjoint-newVarsSpecialSingleton :: String -> Int -> Int -> String-newVarsSpecialSingleton v _ 1 = v-newVarsSpecialSingleton v start n = newVars v start n--showFieldNames :: DynFlags -> PprStyle -> String -> [G.Name] -> String-showFieldNames _ _ _ [] = "" -- This should never happen-showFieldNames dflag style v (x:xs) = let fName = showName dflag style x- fAcc = fName ++ " = " ++ v ++ "_" ++ fName- in case xs of- [] -> fAcc- _ -> fAcc ++ ", " ++ showFieldNames dflag style v xs--------------------------------------------------------------------- c. Code for performing the case splitting--genCaseSplitTextFile :: GhcMonad m => FilePath -> SplitToTextInfo -> m String-genCaseSplitTextFile file info = liftIO $ do- text <- T.readFile file- return $ getCaseSplitText (T.lines text) info--getCaseSplitText :: [T.Text] -> SplitToTextInfo -> String-getCaseSplitText text (SplitToTextInfo { sVarName = sVN, sBindingSpan = sBS- , sVarSpan = sVS, sTycons = sT }) =- let bindingText = getBindingText text sBS- difference = srcSpanDifference sBS sVS- replaced = concatMap (replaceVarWithTyCon bindingText difference sVN) sT- in T.unpack $ T.intercalate (T.pack "\n") replaced--getBindingText :: [T.Text] -> SrcSpan -> [T.Text]-getBindingText text srcSpan =- let Just (sl,sc,el,ec) = Gap.getSrcSpan srcSpan- lines_ = drop (sl - 1) $ take el text- in if sl == el- then -- only one line- [T.drop (sc - 1) $ T.take ec $ head lines_]- else -- several lines- let (first,rest,last_) = (head lines_, tail $ init lines_, last lines_)- in T.drop (sc - 1) first : rest ++ [T.take ec last_]--srcSpanDifference :: SrcSpan -> SrcSpan -> (Int,Int,Int,Int)-srcSpanDifference b v =- let Just (bsl,bsc,_ ,_) = Gap.getSrcSpan b- Just (vsl,vsc,vel,vec) = Gap.getSrcSpan v- in (vsl - bsl, vsc - bsc, vel - bsl, vec - bsc) -- assume variable in one line--replaceVarWithTyCon :: [T.Text] -> (Int,Int,Int,Int) -> String -> String -> [T.Text]-replaceVarWithTyCon text (vsl,vsc,_,vec) varname tycon =- let tycon' = if ' ' `elem` tycon || ':' `elem` tycon then "(" ++ tycon ++ ")" else tycon- lengthDiff = length tycon' - length varname- tycon'' = T.pack $ if lengthDiff < 0 then tycon' ++ replicate (-lengthDiff) ' ' else tycon'- spacesToAdd = if lengthDiff < 0 then 0 else lengthDiff- in zipWith (\n line -> if n < vsl- then line -- before variable starts- else if n == vsl- then T.take vsc line `T.append` tycon'' `T.append` T.drop vec line- else T.replicate spacesToAdd (T.pack " ") `T.append` line)- [0 ..] text
− Language/Haskell/GhcMod/Check.hs
@@ -1,54 +0,0 @@-module Language.Haskell.GhcMod.Check (- checkSyntax- , check- , expandTemplate- , expand- ) where--import Control.Applicative ((<$>))-import Language.Haskell.GhcMod.DynFlags-import qualified Language.Haskell.GhcMod.Gap as Gap-import qualified GHC as G-import Language.Haskell.GhcMod.Logger-import Language.Haskell.GhcMod.Monad (IOish, GhcModT, overrideGhcUserOptions)-import Language.Haskell.GhcMod.Target (setTargetFiles)---------------------------------------------------------------------- | Checking syntax of a target file using GHC.--- Warnings and errors are returned.-checkSyntax :: IOish m- => [FilePath] -- ^ The target files.- -> GhcModT m String-checkSyntax [] = return ""-checkSyntax files = either id id <$> check files---------------------------------------------------------------------- | Checking syntax of a target file using GHC.--- Warnings and errors are returned.-check :: IOish m- => [FilePath] -- ^ The target files.- -> GhcModT m (Either String String)-check fileNames = overrideGhcUserOptions $ \ghcOpts -> do- withLogger (setAllWarningFlags . setNoMaxRelevantBindings . Gap.setWarnTypedHoles . Gap.setDeferTypeErrors) $ do- _ <- G.setSessionDynFlags =<< addCmdOpts ghcOpts =<< G.getSessionDynFlags- setTargetFiles fileNames---------------------------------------------------------------------- | Expanding Haskell Template.-expandTemplate :: IOish m- => [FilePath] -- ^ The target files.- -> GhcModT m String-expandTemplate [] = return ""-expandTemplate files = either id id <$> expand files---------------------------------------------------------------------- | Expanding Haskell Template.-expand :: IOish m- => [FilePath] -- ^ The target files.- -> GhcModT m (Either String String)-expand fileNames = withLogger (Gap.setDumpSplices . setNoWarningFlags) $- setTargetFiles fileNames
− Language/Haskell/GhcMod/Convert.hs
@@ -1,135 +0,0 @@-{-# LANGUAGE FlexibleInstances, FlexibleContexts, OverlappingInstances #-}--module Language.Haskell.GhcMod.Convert (convert, convert', emptyResult, whenFound, whenFound') where--import Language.Haskell.GhcMod.Monad-import Language.Haskell.GhcMod.Types--import Control.Applicative ((<$>))--type Builder = String -> String---- |------ >>> replace '"' "\\\"" "foo\"bar" ""--- "foo\\\"bar"-replace :: Char -> String -> String -> Builder-replace _ _ [] = id-replace c cs (x:xs)- | x == c = (cs ++) . replace c cs xs- | otherwise = (x :) . replace c cs xs--inter :: Char -> [Builder] -> Builder-inter _ [] = id-inter c bs = foldr1 (\x y -> x . (c:) . y) bs--convert' :: (ToString a, IOish m) => a -> GhcModT m String-convert' x = flip convert x <$> options--convert :: ToString a => Options -> a -> String-convert opt@Options { outputStyle = LispStyle } x = toLisp opt x "\n"-convert opt@Options { outputStyle = PlainStyle } x- | str == "\n" = ""- | otherwise = str- where- str = toPlain opt x "\n"--class ToString a where- toLisp :: Options -> a -> Builder- toPlain :: Options -> a -> Builder--lineSep :: Options -> String-lineSep opt = interpret lsep- where- interpret s = read $ "\"" ++ s ++ "\""- LineSeparator lsep = lineSeparator opt---- |------ >>> toLisp defaultOptions "fo\"o" ""--- "\"fo\\\"o\""--- >>> toPlain defaultOptions "foo" ""--- "foo"-instance ToString String where- toLisp opt = quote opt- toPlain opt = replace '\n' (lineSep opt)---- |------ >>> toLisp defaultOptions ["foo", "bar", "ba\"z"] ""--- "(\"foo\" \"bar\" \"ba\\\"z\")"--- >>> toPlain defaultOptions ["foo", "bar", "baz"] ""--- "foo\nbar\nbaz"-instance ToString [String] where- toLisp opt = toSexp1 opt- toPlain opt = inter '\n' . map (toPlain opt)---- |------ >>> let inp = [((1,2,3,4),"foo"),((5,6,7,8),"bar")] :: [((Int,Int,Int,Int),String)]--- >>> toLisp defaultOptions inp ""--- "((1 2 3 4 \"foo\") (5 6 7 8 \"bar\"))"--- >>> toPlain defaultOptions inp ""--- "1 2 3 4 \"foo\"\n5 6 7 8 \"bar\""-instance ToString [((Int,Int,Int,Int),String)] where- toLisp opt = toSexp2 . map toS- where- toS x = ('(' :) . tupToString opt x . (')' :)- toPlain opt = inter '\n' . map (tupToString opt)--instance ToString ((Int,Int,Int,Int),String) where- toLisp opt x = ('(' :) . tupToString opt x . (')' :)- toPlain opt x = tupToString opt x--instance ToString ((Int,Int,Int,Int),[String]) where- toLisp opt (x,s) = ('(' :) . fourIntsToString opt x .- (' ' :) . toLisp opt s . (')' :)- toPlain opt (x,s) = fourIntsToString opt x . ('\n' :) . toPlain opt s--instance ToString (String, (Int,Int,Int,Int),[String]) where- toLisp opt (s,x,y) = toSexp2 [toLisp opt s, ('(' :) . fourIntsToString opt x . (')' :), toLisp opt y]- toPlain opt (s,x,y) = inter '\n' [toPlain opt s, fourIntsToString opt x, toPlain opt y]--toSexp1 :: Options -> [String] -> Builder-toSexp1 opt ss = ('(' :) . inter ' ' (map (quote opt) ss) . (')' :)--toSexp2 :: [Builder] -> Builder-toSexp2 ss = ('(' :) . inter ' ' ss . (')' :)--fourIntsToString :: Options -> (Int,Int,Int,Int) -> Builder-fourIntsToString _ (a,b,c,d) = (show a ++) . (' ' :)- . (show b ++) . (' ' :)- . (show c ++) . (' ' :)- . (show d ++)--tupToString :: Options -> ((Int,Int,Int,Int),String) -> Builder-tupToString opt ((a,b,c,d),s) = (show a ++) . (' ' :)- . (show b ++) . (' ' :)- . (show c ++) . (' ' :)- . (show d ++) . (' ' :)- . quote opt s -- fixme: quote is not necessary--quote :: Options -> String -> Builder-quote opt str = ("\"" ++) . (quote' str ++) . ("\"" ++)- where- lsep = lineSep opt- quote' [] = []- quote' (x:xs)- | x == '\n' = lsep ++ quote' xs- | x == '\\' = "\\\\" ++ quote' xs- | x == '"' = "\\\"" ++ quote' xs- | otherwise = x : quote' xs---------------------------------------------------------------------- Empty result to be returned when no info can be gathered-emptyResult :: Monad m => Options -> m String-emptyResult opt = return $ convert opt ([] :: [String])---- Return an emptyResult when Nothing-whenFound :: (Monad m, ToString b) => Options -> m (Maybe a) -> (a -> b) -> m String-whenFound opt from f = maybe (emptyResult opt) (return . convert opt . f) =<< from---- Return an emptyResult when Nothing, inside a monad-whenFound' :: (Monad m, ToString b) => Options -> m (Maybe a) -> (a -> m b) -> m String-whenFound' opt from f = maybe (emptyResult opt) (\x -> do y <- f x ; return (convert opt y)) =<< from
− Language/Haskell/GhcMod/Cradle.hs
@@ -1,109 +0,0 @@-module Language.Haskell.GhcMod.Cradle (- findCradle- , findCradle'- , findCradleWithoutSandbox- ) where--import Language.Haskell.GhcMod.Types-import Language.Haskell.GhcMod.GhcPkg--import Control.Applicative ((<$>))-import qualified Control.Exception as E-import Control.Exception.IOChoice ((||>))-import Control.Monad (filterM)-import Data.List (isSuffixOf)-import System.Directory (getCurrentDirectory, getDirectoryContents, doesFileExist)-import System.FilePath ((</>), takeDirectory)---------------------------------------------------------------------- | Finding 'Cradle'.--- Find a cabal file by tracing ancestor directories.--- Find a sandbox according to a cabal sandbox config--- in a cabal directory.-findCradle :: IO Cradle-findCradle = findCradle' =<< getCurrentDirectory--findCradle' :: FilePath -> IO Cradle-findCradle' dir = cabalCradle dir ||> sandboxCradle dir ||> plainCradle dir--cabalCradle :: FilePath -> IO Cradle-cabalCradle wdir = do- (rdir,cfile) <- cabalDir wdir- pkgDbStack <- getPackageDbStack rdir- return Cradle {- cradleCurrentDir = wdir- , cradleRootDir = rdir- , cradleCabalFile = Just cfile- , cradlePkgDbStack = pkgDbStack- }--sandboxCradle :: FilePath -> IO Cradle-sandboxCradle wdir = do- rdir <- getSandboxDir wdir- pkgDbStack <- getPackageDbStack rdir- return Cradle {- cradleCurrentDir = wdir- , cradleRootDir = rdir- , cradleCabalFile = Nothing- , cradlePkgDbStack = pkgDbStack- }--plainCradle :: FilePath -> IO Cradle-plainCradle wdir = return Cradle {- cradleCurrentDir = wdir- , cradleRootDir = wdir- , cradleCabalFile = Nothing- , cradlePkgDbStack = [GlobalDb, UserDb]- }---- Just for testing-findCradleWithoutSandbox :: IO Cradle-findCradleWithoutSandbox = do- cradle <- findCradle- return cradle { cradlePkgDbStack = [GlobalDb]} -- FIXME--------------------------------------------------------------------cabalSuffix :: String-cabalSuffix = ".cabal"--cabalSuffixLength :: Int-cabalSuffixLength = length cabalSuffix---- Finding a Cabal file up to the root directory--- Input: a directly to investigate--- Output: (the path to the directory containing a Cabal file--- ,the path to the Cabal file)-cabalDir :: FilePath -> IO (FilePath,FilePath)-cabalDir dir = do- cnts <- getCabalFiles dir- case cnts of- [] | dir' == dir -> E.throwIO $ userError "cabal files not found"- | otherwise -> cabalDir dir'- cfile:_ -> return (dir,dir </> cfile)- where- dir' = takeDirectory dir--getCabalFiles :: FilePath -> IO [FilePath]-getCabalFiles dir = getFiles >>= filterM doesCabalFileExist- where- isCabal name = cabalSuffix `isSuffixOf` name- && length name > cabalSuffixLength- getFiles = filter isCabal <$> getDirectoryContents dir- doesCabalFileExist file = doesFileExist $ dir </> file--------------------------------------------------------------------getSandboxDir :: FilePath -> IO FilePath-getSandboxDir dir = do- exist <- doesFileExist sfile- if exist then- return dir- else if dir == dir' then- E.throwIO $ userError "sandbox not found"- else- getSandboxDir dir'- where- sfile = dir </> "cabal.sandbox.config"- dir' = takeDirectory dir
− Language/Haskell/GhcMod/Debug.hs
@@ -1,41 +0,0 @@-module Language.Haskell.GhcMod.Debug (debugInfo, rootInfo) where--import Control.Applicative ((<$>))-import Data.List (intercalate)-import Data.Maybe (isJust, fromJust)-import Language.Haskell.GhcMod.Convert-import Language.Haskell.GhcMod.Monad-import Language.Haskell.GhcMod.Types-import Language.Haskell.GhcMod.Internal---------------------------------------------------------------------- | Obtaining debug information.-debugInfo :: IOish m => GhcModT m String-debugInfo = cradle >>= \c -> convert' =<< do- CompilerOptions gopts incDir pkgs <-- if isJust $ cradleCabalFile c then- fromCabalFile c ||> simpleCompilerOption- else- simpleCompilerOption- return [- "Root directory: " ++ cradleRootDir c- , "Current directory: " ++ cradleCurrentDir c- , "Cabal file: " ++ show (cradleCabalFile c)- , "GHC options: " ++ unwords gopts- , "Include directories: " ++ unwords incDir- , "Dependent packages: " ++ intercalate ", " (map showPkg pkgs)- , "System libraries: " ++ ghcLibDir- ]- where- simpleCompilerOption = options >>= \op ->- return $ CompilerOptions (ghcUserOptions op) [] []- fromCabalFile c = options >>= \opts -> do- pkgDesc <- parseCabalFile c $ fromJust $ cradleCabalFile c- getCompilerOptions (ghcUserOptions opts) c pkgDesc---------------------------------------------------------------------- | Obtaining root information.-rootInfo :: IOish m => GhcModT m String-rootInfo = convert' =<< cradleRootDir <$> cradle
− Language/Haskell/GhcMod/Doc.hs
@@ -1,21 +0,0 @@-module Language.Haskell.GhcMod.Doc where--import GHC (DynFlags, GhcMonad)-import qualified GHC as G-import Language.Haskell.GhcMod.Gap (withStyle, showDocWith)-import Outputable (SDoc, PprStyle, mkUserStyle, Depth(AllTheWay), neverQualify)-import Pretty (Mode(..))--showPage :: DynFlags -> PprStyle -> SDoc -> String-showPage dflag style = showDocWith dflag PageMode . withStyle dflag style--showOneLine :: DynFlags -> PprStyle -> SDoc -> String-showOneLine dflag style = showDocWith dflag OneLineMode . withStyle dflag style--getStyle :: GhcMonad m => m PprStyle-getStyle = do- unqual <- G.getPrintUnqual- return $ mkUserStyle unqual AllTheWay--styleUnqualified :: PprStyle-styleUnqualified = mkUserStyle neverQualify AllTheWay
− Language/Haskell/GhcMod/DynFlags.hs
@@ -1,121 +0,0 @@-{-# LANGUAGE CPP #-}--module Language.Haskell.GhcMod.DynFlags where--import Control.Applicative ((<$>))-import Control.Monad (void)-import GHC (DynFlags(..), GhcMode(..), GhcLink(..), HscTarget(..))-import qualified GHC as G-import GHC.Paths (libdir)-import GhcMonad-import qualified Language.Haskell.GhcMod.Gap as Gap-import Language.Haskell.GhcMod.Types-import System.IO.Unsafe (unsafePerformIO)--data Build = CabalPkg | SingleFile deriving Eq--setEmptyLogger :: DynFlags -> DynFlags-setEmptyLogger df = Gap.setLogAction df $ \_ _ _ _ _ -> return ()---- * Fast--- * Friendly to foreign export--- * Not friendly to -XTemplateHaskell and -XPatternSynonyms--- * Uses little memory-setModeSimple :: DynFlags -> DynFlags-setModeSimple df = df {- ghcMode = CompManager- , ghcLink = NoLink- , hscTarget = HscNothing- , optLevel = 0- }---- * Slow--- * Not friendly to foreign export--- * Friendly to -XTemplateHaskell and -XPatternSynonyms--- * Uses lots of memory-setModeIntelligent :: DynFlags -> DynFlags-setModeIntelligent df = df {- ghcMode = CompManager- , ghcLink = LinkInMemory- , hscTarget = HscInterpreted- , optLevel = 0- }--setIncludeDirs :: [IncludeDir] -> DynFlags -> DynFlags-setIncludeDirs idirs df = df { importPaths = idirs }--setBuildEnv :: Build -> DynFlags -> DynFlags-setBuildEnv build = setHideAllPackages build . setCabalPackage build---- | With ghc-7.8 this option simply makes GHC print a message suggesting users--- add hiddend packages to the build-depends field in their cabal file when the--- user tries to import a module form a hidden package.-setCabalPackage :: Build -> DynFlags -> DynFlags-setCabalPackage CabalPkg df = Gap.setCabalPkg df-setCabalPackage _ df = df---- | Enable hiding of all package not explicitly exposed (like Cabal does)-setHideAllPackages :: Build -> DynFlags -> DynFlags-setHideAllPackages CabalPkg df = Gap.setHideAllPackages df-setHideAllPackages _ df = df---- | Parse command line ghc options and add them to the 'DynFlags' passed-addCmdOpts :: GhcMonad m => [GHCOption] -> DynFlags -> m DynFlags-addCmdOpts cmdOpts df =- tfst <$> G.parseDynamicFlags df (map G.noLoc cmdOpts)- where- tfst (a,_,_) = a---------------------------------------------------------------------- | Return the 'DynFlags' currently in use in the GHC session.-getDynamicFlags :: IO DynFlags-getDynamicFlags = G.runGhc (Just libdir) G.getSessionDynFlags--withDynFlags :: GhcMonad m- => (DynFlags -> DynFlags)- -> m a- -> m a-withDynFlags setFlags body = G.gbracket setup teardown (\_ -> body)- where- setup = do- dflags <- G.getSessionDynFlags- void $ G.setSessionDynFlags (setFlags dflags)- return dflags- teardown = void . G.setSessionDynFlags--withCmdFlags :: GhcMonad m => [GHCOption] -> m a -> m a-withCmdFlags flags body = G.gbracket setup teardown (\_ -> body)- where- setup = do- dflags <- G.getSessionDynFlags- void $ G.setSessionDynFlags =<< addCmdOpts flags dflags- return dflags- teardown = void . G.setSessionDynFlags---------------------------------------------------------------------- | Set 'DynFlags' equivalent to "-w:".-setNoWarningFlags :: DynFlags -> DynFlags-setNoWarningFlags df = df { warningFlags = Gap.emptyWarnFlags}---- | Set 'DynFlags' equivalent to "-Wall".-setAllWarningFlags :: DynFlags -> DynFlags-setAllWarningFlags df = df { warningFlags = allWarningFlags }--allWarningFlags :: Gap.WarnFlags-allWarningFlags = unsafePerformIO $- G.runGhc (Just libdir) $ do- df <- G.getSessionDynFlags- df' <- addCmdOpts ["-Wall"] df- return $ G.warningFlags df'---------------------------------------------------------------------- | Set 'DynFlags' equivalent to "-fno-max-relevant-bindings".-setNoMaxRelevantBindings :: DynFlags -> DynFlags-#if __GLASGOW_HASKELL__ >= 708-setNoMaxRelevantBindings df = df { maxRelevantBinds = Nothing }-#else-setNoMaxRelevantBindings = id-#endif
− Language/Haskell/GhcMod/Error.hs
@@ -1,41 +0,0 @@-{-# LANGUAGE TypeFamilies, ScopedTypeVariables #-}-module Language.Haskell.GhcMod.Error (- GhcModError(..)- , modifyError- , modifyError'- , tryFix- , module Control.Monad.Error- , module Exception- ) where--import Control.Monad.Error (MonadError(..), Error(..))-import Exception--data GhcModError = GMENoMsg- -- ^ Unknown error- | GMEString String- -- ^ Some Error with a message. These are produced mostly by- -- 'fail' calls on GhcModT.- | GMECabalConfigure GhcModError- -- ^ Configuring a cabal project failed.- | GMECabalFlags GhcModError- -- ^ Retrieval of the cabal configuration flags failed.- | GMEProcess [String] GhcModError- -- ^ Launching an operating system process failed. The first- -- field is the command.- deriving (Eq,Show)--instance Error GhcModError where- noMsg = GMENoMsg- strMsg = GMEString--modifyError :: MonadError e m => (e -> e) -> m a -> m a-modifyError f action = action `catchError` \e -> throwError $ f e--infixr 0 `modifyError'`-modifyError' :: MonadError e m => m a -> (e -> e) -> m a-modifyError' = flip modifyError--tryFix :: MonadError e m => m a -> (e -> m ()) -> m a-tryFix action fix = do- action `catchError` \e -> fix e >> action
− Language/Haskell/GhcMod/FillSig.hs
@@ -1,484 +0,0 @@-{-# LANGUAGE CPP, MultiParamTypeClasses, FunctionalDependencies #-}-{-# LANGUAGE FlexibleInstances #-}--module Language.Haskell.GhcMod.FillSig (- sig- , refine- , auto- ) where--import Data.Char (isSymbol)-import Data.Function (on)-import Data.List (find, nub, sortBy)-import qualified Data.Map as M-import Data.Maybe (isJust, catMaybes)-import Exception (ghandle, SomeException(..))-import GHC (GhcMonad, Id, ParsedModule(..), TypecheckedModule(..), DynFlags,- SrcSpan, Type, GenLocated(L))-import qualified GHC as G-import qualified Name as G-import qualified Language.Haskell.GhcMod.Gap as Gap-import Language.Haskell.GhcMod.Convert-import Language.Haskell.GhcMod.Monad-import Language.Haskell.GhcMod.SrcUtils-import Language.Haskell.GhcMod.Types-import Outputable (PprStyle)-import qualified Type as Ty-import qualified HsBinds as Ty-import qualified Class as Ty-import qualified Var as Ty-import qualified HsPat as Ty-import qualified Language.Haskell.Exts.Annotated as HE-import Djinn.GHC--------------------------------------------------------------------- INTIAL CODE FROM FUNCTION OR INSTANCE SIGNATURE--------------------------------------------------------------------- Possible signatures we can find: function or instance-data SigInfo- = Signature SrcSpan [G.RdrName] (G.HsType G.RdrName)- | InstanceDecl SrcSpan G.Class- | TyFamDecl SrcSpan G.RdrName TyFamType {- True if closed -} [G.RdrName]---- Signature for fallback operation via haskell-src-exts-data HESigInfo- = HESignature HE.SrcSpan [HE.Name HE.SrcSpanInfo] (HE.Type HE.SrcSpanInfo)- | HEFamSignature- HE.SrcSpan- TyFamType- (HE.Name HE.SrcSpanInfo)- [HE.Name HE.SrcSpanInfo]--data TyFamType = Closed | Open | Data-initialTyFamString :: TyFamType -> (String, String)-initialTyFamString Closed = ("instance", "")-initialTyFamString Open = ("function", "type instance ")-initialTyFamString Data = ("function", "data instance ")---- | Create a initial body from a signature.-sig :: IOish m- => FilePath -- ^ A target file.- -> Int -- ^ Line number.- -> Int -- ^ Column number.- -> GhcModT m String-sig file lineNo colNo = ghandle handler body- where- body = inModuleContext file $ \dflag style -> do- opt <- options- modSum <- Gap.fileModSummary file- whenFound opt (getSignature modSum lineNo colNo) $ \s -> case s of- Signature loc names ty ->- ("function", fourInts loc, map (initialBody dflag style ty) names)- InstanceDecl loc cls ->- ("instance", fourInts loc, map (\x -> initialBody dflag style (G.idType x) x)- (Ty.classMethods cls))- TyFamDecl loc name flavour vars ->- let (rTy, initial) = initialTyFamString flavour- in (rTy, fourInts loc, [initial ++ initialFamBody dflag style name vars])-- handler (SomeException _) = do- opt <- options- -- Code cannot be parsed by ghc module- -- Fallback: try to get information via haskell-src-exts- whenFound opt (getSignatureFromHE file lineNo colNo) $ \x -> case x of- HESignature loc names ty ->- ("function", fourIntsHE loc, map (initialBody undefined undefined ty) names)- HEFamSignature loc flavour name vars ->- let (rTy, initial) = initialTyFamString flavour- in (rTy, fourIntsHE loc, [initial ++ initialFamBody undefined undefined name vars])--------------------------------------------------------------------- a. Code for getting the information---- Get signature from ghc parsing and typechecking-getSignature :: GhcMonad m => G.ModSummary -> Int -> Int -> m (Maybe SigInfo)-getSignature modSum lineNo colNo = do- p@ParsedModule{pm_parsed_source = ps} <- G.parseModule modSum- -- Inspect the parse tree to find the signature- case listifyParsedSpans ps (lineNo, colNo) :: [G.LHsDecl G.RdrName] of- [L loc (G.SigD (Ty.TypeSig names (L _ ty)))] ->- -- We found a type signature- return $ Just $ Signature loc (map G.unLoc names) ty- [L _ (G.InstD _)] -> do- -- We found an instance declaration- TypecheckedModule{tm_renamed_source = Just tcs- ,tm_checked_module_info = minfo} <- G.typecheckModule p- let lst = listifyRenamedSpans tcs (lineNo, colNo)- case Gap.getClass lst of- Just (clsName,loc) -> obtainClassInfo minfo clsName loc- _ -> return Nothing-#if __GLASGOW_HASKELL__ >= 708- [L loc (G.TyClD (G.FamDecl (G.FamilyDecl info (L _ name) (G.HsQTvs _ vars) _)))] -> do-#elif __GLASGOW_HASKELL__ >= 706- [L loc (G.TyClD (G.TyFamily info (L _ name) (G.HsQTvs _ vars) _))] -> do-#else- [L loc (G.TyClD (G.TyFamily info (L _ name) vars _))] -> do-#endif-#if __GLASGOW_HASKELL__ >= 708- let flavour = case info of- G.ClosedTypeFamily _ -> Closed- G.OpenTypeFamily -> Open- G.DataFamily -> Data-#else- let flavour = case info of -- Closed type families where introduced in GHC 7.8- G.TypeFamily -> Open- G.DataFamily -> Data-#endif-#if __GLASGOW_HASKELL__ >= 706- getTyFamVarName x = case x of- L _ (G.UserTyVar n) -> n- L _ (G.KindedTyVar n _) -> n-#else- getTyFamVarName x = case x of -- In GHC 7.4, HsTyVarBndr's have an extra arg- L _ (G.UserTyVar n _) -> n- L _ (G.KindedTyVar n _ _) -> n-#endif- in return $ Just (TyFamDecl loc name flavour $ map getTyFamVarName vars)- _ -> return Nothing- where obtainClassInfo :: GhcMonad m => G.ModuleInfo -> G.Name -> SrcSpan -> m (Maybe SigInfo)- obtainClassInfo minfo clsName loc = do- tyThing <- G.modInfoLookupName minfo clsName- return $ do Ty.ATyCon clsCon <- tyThing -- In Maybe- cls <- G.tyConClass_maybe clsCon- return $ InstanceDecl loc cls---- Get signature from haskell-src-exts-getSignatureFromHE :: GhcMonad m => FilePath -> Int -> Int -> m (Maybe HESigInfo)-getSignatureFromHE file lineNo colNo = do- presult <- liftIO $ HE.parseFile file- return $ case presult of- HE.ParseOk (HE.Module _ _ _ _ mdecls) -> do- decl <- find (typeSigInRangeHE lineNo colNo) mdecls- case decl of- HE.TypeSig (HE.SrcSpanInfo s _) names ty ->- return $ HESignature s names ty-- HE.TypeFamDecl (HE.SrcSpanInfo s _) declHead _ ->- let (name, tys) = dHeadTyVars declHead in- return $ HEFamSignature s Open name (map cleanTyVarBind tys)-- HE.DataFamDecl (HE.SrcSpanInfo s _) _ declHead _ ->- let (name, tys) = dHeadTyVars declHead in- return $ HEFamSignature s Open name (map cleanTyVarBind tys)- _ -> fail ""- _ -> Nothing- where cleanTyVarBind (HE.KindedVar _ n _) = n- cleanTyVarBind (HE.UnkindedVar _ n) = n--#if MIN_VERSION_haskell_src_exts(1,16,0)- dHeadTyVars :: HE.DeclHead l -> (HE.Name l, [HE.TyVarBind l])- dHeadTyVars (HE.DHead _ name) = (name, [])- dHeadTyVars (HE.DHApp _ r ty) = (++[ty]) `fmap` (dHeadTyVars r)- dHeadTyVars (HE.DHInfix _ ty name) = (name, [ty])- dHeadTyVars (HE.DHParen _ r) = dHeadTyVars r-#else- dHeadTyVars :: HE.DeclHead l -> (HE.Name l, [HE.TyVarBind l])- dHeadTyVars (HE.DHead _ n tys) = (n, tys)-#endif--------------------------------------------------------------------- b. Code for generating initial code---- A list of function arguments, and whether they are functions or normal--- arguments is built from either a function signature or an instance signature-data FnArg = FnArgFunction | FnArgNormal | FnExplicitName String--initialBody :: FnArgsInfo ty name => DynFlags -> PprStyle -> ty -> name -> String-initialBody dflag style ty name =- initialBody' (getFnName dflag style name) (getFnArgs ty)--initialBody' :: String -> [FnArg] -> String-initialBody' fname args =- initialHead fname args ++ " = " ++ n ++ "_body"- where n = if isSymbolName fname then "" else '_':fname--initialFamBody :: FnArgsInfo ty name- => DynFlags -> PprStyle -> name -> [name] -> String-initialFamBody dflag style name args =- initialHead fnName fnArgs ++ " = ()"- where fnName = getFnName dflag style name- fnArgs = map (FnExplicitName . getFnName dflag style) args--initialHead :: String -> [FnArg] -> String-initialHead fname args =- case initialBodyArgs args infiniteVars infiniteFns of- [] -> fname- arglist -> if isSymbolName fname- then head arglist ++ " " ++ fname ++ " " ++ unwords (tail arglist)- else fname ++ " " ++ unwords arglist--initialBodyArgs :: [FnArg] -> [String] -> [String] -> [String]-initialBodyArgs [] _ _ = []-initialBodyArgs (FnArgFunction:xs) vs (f:fs) = f : initialBodyArgs xs vs fs-initialBodyArgs (FnArgNormal:xs) (v:vs) fs = v : initialBodyArgs xs vs fs-initialBodyArgs (FnExplicitName n:xs) vs fs = n : initialBodyArgs xs vs fs-initialBodyArgs _ _ _ =- error "initialBodyArgs: This should never happen" -- Lists are infinite--initialHead1 :: String -> [FnArg] -> [String] -> String-initialHead1 fname args elts =- case initialBodyArgs1 args elts of- [] -> fname- arglist -> if isSymbolName fname- then head arglist ++ " " ++ fname ++ " " ++ unwords (tail arglist)- else fname ++ " " ++ unwords arglist--initialBodyArgs1 :: [FnArg] -> [String] -> [String]-initialBodyArgs1 args elts = take (length args) elts---- Getting the initial body of function and instances differ--- This is because for functions we only use the parsed file--- (so the full file doesn't have to be type correct)--- but for instances we need to get information about the class--class FnArgsInfo ty name | ty -> name, name -> ty where- getFnName :: DynFlags -> PprStyle -> name -> String- getFnArgs :: ty -> [FnArg]--instance FnArgsInfo (G.HsType G.RdrName) (G.RdrName) where- getFnName dflag style name = showOccName dflag style $ Gap.occName name- getFnArgs (G.HsForAllTy _ _ _ (L _ iTy)) = getFnArgs iTy- getFnArgs (G.HsParTy (L _ iTy)) = getFnArgs iTy- getFnArgs (G.HsFunTy (L _ lTy) (L _ rTy)) =- (if fnarg lTy then FnArgFunction else FnArgNormal):getFnArgs rTy- where fnarg ty = case ty of- (G.HsForAllTy _ _ _ (L _ iTy)) -> fnarg iTy- (G.HsParTy (L _ iTy)) -> fnarg iTy- (G.HsFunTy _ _) -> True- _ -> False- getFnArgs _ = []--instance FnArgsInfo (HE.Type HE.SrcSpanInfo) (HE.Name HE.SrcSpanInfo) where- getFnName _ _ (HE.Ident _ s) = s- getFnName _ _ (HE.Symbol _ s) = s- getFnArgs (HE.TyForall _ _ _ iTy) = getFnArgs iTy- getFnArgs (HE.TyParen _ iTy) = getFnArgs iTy- getFnArgs (HE.TyFun _ lTy rTy) =- (if fnarg lTy then FnArgFunction else FnArgNormal):getFnArgs rTy- where fnarg ty = case ty of- (HE.TyForall _ _ _ iTy) -> fnarg iTy- (HE.TyParen _ iTy) -> fnarg iTy- (HE.TyFun _ _ _) -> True- _ -> False- getFnArgs _ = []--instance FnArgsInfo Type Id where- getFnName dflag style method = showOccName dflag style $ G.getOccName method- getFnArgs = getFnArgs' . Ty.dropForAlls- where getFnArgs' ty | Just (lTy,rTy) <- Ty.splitFunTy_maybe ty =- maybe (if Ty.isPredTy lTy then getFnArgs' rTy else FnArgNormal:getFnArgs' rTy)- (\_ -> FnArgFunction:getFnArgs' rTy)- $ Ty.splitFunTy_maybe lTy-- getFnArgs' ty | Just (_,iTy) <- Ty.splitForAllTy_maybe ty =- getFnArgs' iTy-- getFnArgs' _ = []---- Infinite supply of variable and function variable names-infiniteVars, infiniteFns :: [String]-infiniteVars = infiniteSupply ["x","y","z","t","u","v","w"]-infiniteFns = infiniteSupply ["f","g","h"]-infiniteSupply :: [String] -> [String]-infiniteSupply initialSupply =- initialSupply ++ concatMap (\n -> map (\v -> v ++ show n) initialSupply)- ([1 .. ] :: [Integer])---- Check whether a String is a symbol name-isSymbolName :: String -> Bool-isSymbolName (c:_) = c `elem` "!#$%&*+./<=>?@\\^|-~" || isSymbol c-isSymbolName [] = error "This should never happen"---------------------------------------------------------------------- REWRITE A HOLE / UNDEFINED VIA A FUNCTION-------------------------------------------------------------------refine :: IOish m- => FilePath -- ^ A target file.- -> Int -- ^ Line number.- -> Int -- ^ Column number.- -> Expression -- ^ A Haskell expression.- -> GhcModT m String-refine file lineNo colNo expr = ghandle handler body- where- body = inModuleContext file $ \dflag style -> do- opt <- options- modSum <- Gap.fileModSummary file- p <- G.parseModule modSum- tcm@TypecheckedModule{tm_typechecked_source = tcs} <- G.typecheckModule p- ety <- G.exprType expr- whenFound opt (findVar dflag style tcm tcs lineNo colNo) $- \(loc, name, rty, paren) ->- let eArgs = getFnArgs ety- rArgs = getFnArgs rty- diffArgs' = length eArgs - length rArgs- diffArgs = if diffArgs' < 0 then 0 else diffArgs'- iArgs = take diffArgs eArgs- text = initialHead1 expr iArgs (infinitePrefixSupply name)- in (fourInts loc, doParen paren text)-- handler (SomeException _) = emptyResult =<< options---- Look for the variable in the specified position-findVar :: GhcMonad m => DynFlags -> PprStyle- -> G.TypecheckedModule -> G.TypecheckedSource- -> Int -> Int -> m (Maybe (SrcSpan, String, Type, Bool))-findVar dflag style tcm tcs lineNo colNo =- let lst = sortBy (cmp `on` G.getLoc) $- listifySpans tcs (lineNo, colNo) :: [G.LHsExpr Id]- in case lst of- e@(L _ (G.HsVar i)):others ->- do tyInfo <- Gap.getType tcm e- let name = getFnName dflag style i- if (name == "undefined" || head name == '_') && isJust tyInfo- then let Just (s,t) = tyInfo- b = case others of -- If inside an App, we need- -- parenthesis- [] -> False- L _ (G.HsApp (L _ a1) (L _ a2)):_ ->- isSearchedVar i a1 || isSearchedVar i a2- _ -> False- in return $ Just (s, name, t, b)- else return Nothing- _ -> return Nothing--infinitePrefixSupply :: String -> [String]-infinitePrefixSupply "undefined" = repeat "undefined"-infinitePrefixSupply p = map (\n -> p ++ "_" ++ show n) ([1 ..] :: [Integer])--doParen :: Bool -> String -> String-doParen False s = s-doParen True s = if ' ' `elem` s then '(':s ++ ")" else s--isSearchedVar :: Id -> G.HsExpr Id -> Bool-isSearchedVar i (G.HsVar i2) = i == i2-isSearchedVar _ _ = False---------------------------------------------------------------------- REFINE AUTOMATICALLY-------------------------------------------------------------------auto :: IOish m- => FilePath -- ^ A target file.- -> Int -- ^ Line number.- -> Int -- ^ Column number.- -> GhcModT m String-auto file lineNo colNo = ghandle handler body- where- body = inModuleContext file $ \dflag style -> do- opt <- options- modSum <- Gap.fileModSummary file- p <- G.parseModule modSum- tcm@TypecheckedModule {- tm_typechecked_source = tcs- , tm_checked_module_info = minfo- } <- G.typecheckModule p- whenFound' opt (findVar dflag style tcm tcs lineNo colNo) $ \(loc, _name, rty, paren) -> do- topLevel <- getEverythingInTopLevel minfo- let (f,pats) = getPatsForVariable tcs (lineNo,colNo)- -- Remove self function to prevent recursion, and id to trim- -- cases- filterFn (n,_) = let funName = G.getOccString n- recName = G.getOccString (G.getName f)- in funName `notElem` recName:notWantedFuns- -- Find without using other functions in top-level- localBnds = M.unions $- map (\(L _ pat) -> getBindingsForPat pat) pats- lbn = filter filterFn (M.toList localBnds)- djinnsEmpty <- djinn True (Just minfo) lbn rty (Max 10) 100000- let -- Find with the entire top-level- almostEnv = M.toList $ M.union localBnds topLevel- env = filter filterFn almostEnv- djinns <- djinn True (Just minfo) env rty (Max 10) 100000- return ( fourInts loc- , map (doParen paren) $ nub (djinnsEmpty ++ djinns))-- handler (SomeException _) = emptyResult =<< options---- Functions we do not want in completions-notWantedFuns :: [String]-notWantedFuns = ["id", "asTypeOf", "const"]---- Get all things defined in top-level-getEverythingInTopLevel :: GhcMonad m => G.ModuleInfo -> m (M.Map G.Name Type)-getEverythingInTopLevel m = do- let modInfo = tyThingsToInfo (G.modInfoTyThings m)- topNames = G.modInfoTopLevelScope m- case topNames of- Just topNames' -> do topThings <- mapM G.lookupGlobalName topNames'- let topThings' = catMaybes topThings- topInfo = tyThingsToInfo topThings'- return $ M.union modInfo topInfo- Nothing -> return modInfo--tyThingsToInfo :: [Ty.TyThing] -> M.Map G.Name Type-tyThingsToInfo [] = M.empty-tyThingsToInfo (G.AnId i : xs) =- M.insert (G.getName i) (Ty.varType i) (tyThingsToInfo xs)--- Getting information about constructors is not needed--- because they will be added by djinn-ghc when traversing types--- #if __GLASGOW_HASKELL__ >= 708--- tyThingToInfo (G.AConLike (G.RealDataCon con)) = return [(Ty.dataConName con, Ty.dataConUserType con)]--- #else--- tyThingToInfo (G.AConLike con) = return [(Ty.dataConName con, Ty.dataConUserType con)]--- #endif-tyThingsToInfo (_:xs) = tyThingsToInfo xs---- Find the Id of the function and the pattern where the hole is located-getPatsForVariable :: G.TypecheckedSource -> (Int,Int) -> (Id, [Ty.LPat Id])-getPatsForVariable tcs (lineNo, colNo) =- let (L _ bnd:_) = sortBy (cmp `on` G.getLoc) $- listifySpans tcs (lineNo, colNo) :: [G.LHsBind Id]- in case bnd of- G.PatBind { Ty.pat_lhs = L ploc pat } -> case pat of- Ty.ConPatIn (L _ i) _ -> (i, [L ploc pat])- _ -> (error "This should never happen", [])- G.FunBind { Ty.fun_id = L _ funId } ->- let m = sortBy (cmp `on` G.getLoc) $ listifySpans tcs (lineNo, colNo)-#if __GLASGOW_HASKELL__ >= 708- :: [G.LMatch Id (G.LHsExpr Id)]-#else- :: [G.LMatch Id]-#endif- (L _ (G.Match pats _ _):_) = m- in (funId, pats)- _ -> (error "This should never happen", [])--getBindingsForPat :: Ty.Pat Id -> M.Map G.Name Type-getBindingsForPat (Ty.VarPat i) = M.singleton (G.getName i) (Ty.varType i)-getBindingsForPat (Ty.LazyPat (L _ l)) = getBindingsForPat l-getBindingsForPat (Ty.BangPat (L _ b)) = getBindingsForPat b-getBindingsForPat (Ty.AsPat (L _ a) (L _ i)) =- M.insert (G.getName a) (Ty.varType a) (getBindingsForPat i)-#if __GLASGOW_HASKELL__ >= 708-getBindingsForPat (Ty.ListPat l _ _) =- M.unions $ map (\(L _ i) -> getBindingsForPat i) l-#else-getBindingsForPat (Ty.ListPat l _) =- M.unions $ map (\(L _ i) -> getBindingsForPat i) l-#endif-getBindingsForPat (Ty.TuplePat l _ _) =- M.unions $ map (\(L _ i) -> getBindingsForPat i) l-getBindingsForPat (Ty.PArrPat l _) =- M.unions $ map (\(L _ i) -> getBindingsForPat i) l-getBindingsForPat (Ty.ViewPat _ (L _ i) _) = getBindingsForPat i-getBindingsForPat (Ty.SigPatIn (L _ i) _) = getBindingsForPat i-getBindingsForPat (Ty.SigPatOut (L _ i) _) = getBindingsForPat i-getBindingsForPat (Ty.ConPatIn (L _ i) d) =- M.insert (G.getName i) (Ty.varType i) (getBindingsForRecPat d)-getBindingsForPat (Ty.ConPatOut { Ty.pat_args = d }) = getBindingsForRecPat d-getBindingsForPat _ = M.empty--getBindingsForRecPat :: Ty.HsConPatDetails Id -> M.Map G.Name Type-getBindingsForRecPat (Ty.PrefixCon args) =- M.unions $ map (\(L _ i) -> getBindingsForPat i) args-getBindingsForRecPat (Ty.InfixCon (L _ a1) (L _ a2)) =- M.union (getBindingsForPat a1) (getBindingsForPat a2)-getBindingsForRecPat (Ty.RecCon (Ty.HsRecFields { Ty.rec_flds = fields })) =- getBindingsForRecFields fields- where getBindingsForRecFields [] = M.empty- getBindingsForRecFields (Ty.HsRecField {Ty.hsRecFieldArg = (L _ a)}:fs) =- M.union (getBindingsForPat a) (getBindingsForRecFields fs)
− Language/Haskell/GhcMod/Find.hs
@@ -1,207 +0,0 @@-{-# LANGUAGE CPP #-}--module Language.Haskell.GhcMod.Find-#ifndef SPEC- (- Symbol- , SymbolDb- , loadSymbolDb- , lookupSymbol- , dumpSymbol- , findSymbol- , lookupSym- )-#endif- where--import Config (cProjectVersion,cTargetPlatformString)-import Control.Applicative ((<$>))-import Control.Monad (when, void)-import Control.Monad.Error.Class-import Data.Function (on)-import Data.List (groupBy, sort)-import Data.List.Split (splitOn)-import Data.Maybe (fromMaybe)-import DynFlags (DynFlags(..), systemPackageConfig)-import Exception (handleIO)-import qualified GHC as G-import Language.Haskell.GhcMod.Convert-import Language.Haskell.GhcMod.Monad-import Language.Haskell.GhcMod.Utils-import Language.Haskell.GhcMod.Types-import Name (getOccString)-import System.Directory (doesDirectoryExist, getAppUserDataDirectory, doesFileExist, getModificationTime)-import System.FilePath ((</>), takeDirectory)-import System.IO-import System.Environment--#ifndef MIN_VERSION_containers-#define MIN_VERSION_containers(x,y,z) 1-#endif--#if MIN_VERSION_containers(0,5,0)-import Data.Map.Strict (Map)-import qualified Data.Map.Strict as M-#else-import Data.Map (Map)-import qualified Data.Map as M-#endif---------------------------------------------------------------------- | Type of function and operation names.-type Symbol = String--- | Database from 'Symbol' to \['ModuleString'\].-newtype SymbolDb = SymbolDb (Map Symbol [ModuleString])- deriving (Show)---------------------------------------------------------------------- | When introducing incompatible changes to the 'symbolCache' file format--- increment this version number.-symbolCacheVersion :: Integer-symbolCacheVersion = 0---- | Filename of the symbol table cache file.-symbolCache :: String-symbolCache = "ghc-mod-"++ show symbolCacheVersion ++".cache"--packageCache :: String-packageCache = "package.cache"--packageConfDir :: String-packageConfDir = "package.conf.d"---------------------------------------------------------------------- | Looking up 'SymbolDb' with 'Symbol' to \['ModuleString'\]--- which will be concatenated. 'loadSymbolDb' is called internally.-findSymbol :: IOish m => Symbol -> GhcModT m String-findSymbol sym = loadSymbolDb >>= lookupSymbol sym---- | Looking up 'SymbolDb' with 'Symbol' to \['ModuleString'\]--- which will be concatenated.-lookupSymbol :: IOish m => Symbol -> SymbolDb -> GhcModT m String-lookupSymbol sym db = convert' $ lookupSym sym db--lookupSym :: Symbol -> SymbolDb -> [ModuleString]-lookupSym sym (SymbolDb db) = fromMaybe [] $ M.lookup sym db--------------------------------------------------------------------- | Loading a file and creates 'SymbolDb'.-loadSymbolDb :: (IOish m, MonadError GhcModError m) => m SymbolDb-loadSymbolDb = SymbolDb <$> readSymbolDb---- | Returns the path to the currently running ghc-mod executable. With ghc<7.6--- this is a guess but >=7.6 uses 'getExecutablePath'.-ghcModExecutable :: IO FilePath-#ifndef SPEC-ghcModExecutable = do- dir <- getExecutablePath'- return $ dir </> "ghc-mod"-#else-ghcModExecutable = do _ <- getExecutablePath' -- get rid of unused warning when- -- compiling spec- return "dist/build/ghc-mod/ghc-mod"-#endif- where- getExecutablePath' :: IO FilePath-# if __GLASGOW_HASKELL__ >= 706- getExecutablePath' = takeDirectory <$> getExecutablePath-# else- getExecutablePath' = return ""-# endif--readSymbolDb :: (IOish m, MonadError GhcModError m) => m (Map Symbol [ModuleString])-readSymbolDb = do- ghcMod <- liftIO ghcModExecutable- file <- chop <$> readProcess' ghcMod ["dumpsym"]- M.fromAscList . map conv . lines <$> liftIO (readFile file)- where- conv :: String -> (Symbol,[ModuleString])- conv = read- chop "" = ""- chop xs = init xs--------------------------------------------------------------------- used 'ghc-mod dumpsym'--getSymbolCachePath :: IOish m => GhcModT m FilePath-getSymbolCachePath = do- u:_ <- filter (/= GlobalDb) . cradlePkgDbStack <$> cradle- Just db <- (liftIO . flip resolvePackageDb u) =<< G.getSessionDynFlags- return db- `catchError` const (fail "Couldn't find non-global package database for symbol cache")---- | Dumping a set of ('Symbol',\['ModuleString'\]) to a file--- if the file does not exist or is invalid.--- The file name is printed.--dumpSymbol :: IOish m => GhcModT m String-dumpSymbol = do- dir <- getSymbolCachePath- let cache = dir </> symbolCache- pkgdb = dir </> packageCache-- create <- liftIO $ cache `isNewerThan` pkgdb- when create $ (liftIO . writeSymbolCache cache) =<< getSymbolTable- return $ unlines [cache]--writeSymbolCache :: FilePath- -> [(Symbol,[ModuleString])]- -> IO ()-writeSymbolCache cache sm =- void . withFile cache WriteMode $ \hdl ->- mapM (hPrint hdl) sm--isNewerThan :: FilePath -> FilePath -> IO Bool-isNewerThan ref file = do- exist <- doesFileExist ref- if not exist then- return True- else do- tRef <- getModificationTime ref- tFile <- getModificationTime file- return $ tRef <= tFile -- including equal just in case---- | Browsing all functions in all system/user modules.-getSymbolTable :: IOish m => GhcModT m [(Symbol,[ModuleString])]-getSymbolTable = do- ghcModules <- G.packageDbModules True- moduleInfos <- mapM G.getModuleInfo ghcModules- let modules = do- m <- ghcModules- let moduleName = G.moduleNameString $ G.moduleName m--- modulePkg = G.packageIdString $ G.modulePackageId m- return moduleName-- return $ collectModules- $ extractBindings `concatMap` (moduleInfos `zip` modules)--extractBindings :: (Maybe G.ModuleInfo, ModuleString)- -> [(Symbol, ModuleString)]-extractBindings (Nothing,_) = []-extractBindings (Just inf,mdlname) =- map (\name -> (getOccString name, mdlname)) names- where- names = G.modInfoExports inf--collectModules :: [(Symbol,ModuleString)]- -> [(Symbol,[ModuleString])]-collectModules = map tieup . groupBy ((==) `on` fst) . sort- where- tieup x = (head (map fst x), map snd x)----- Copied from ghc module `Packages' unfortunately it's not exported :/-resolvePackageDb :: DynFlags -> GhcPkgDb -> IO (Maybe FilePath)-resolvePackageDb df GlobalDb = return $ Just (systemPackageConfig df)-resolvePackageDb _ (PackageDb name) = return $ Just name-resolvePackageDb _ UserDb = handleIO (\_ -> return Nothing) $ do- appdir <- getAppUserDataDirectory "ghc"- let dir = appdir </> (target_arch ++ '-':target_os ++ '-':cProjectVersion)- pkgconf = dir </> packageConfDir- exist <- doesDirectoryExist pkgconf- return $ if exist then Just pkgconf else Nothing- where- [target_arch,_,target_os] = splitOn "-" cTargetPlatformString
− Language/Haskell/GhcMod/Flag.hs
@@ -1,13 +0,0 @@-module Language.Haskell.GhcMod.Flag where--import qualified Language.Haskell.GhcMod.Gap as Gap-import Language.Haskell.GhcMod.Convert-import Language.Haskell.GhcMod.Monad---- | Listing GHC flags. (e.g -fno-warn-orphans)--flags :: IOish m => GhcModT m String-flags = convert' [ "-f" ++ prefix ++ option- | option <- Gap.fOptions- , prefix <- ["","no-"]- ]
− Language/Haskell/GhcMod/GHCApi.hs
@@ -1,86 +0,0 @@-{-# LANGUAGE ScopedTypeVariables #-}--module Language.Haskell.GhcMod.GHCApi (- ghcPkgDb- , package- , modules- , findModule- , moduleInfo- , localModuleInfo- , bindings- ) where--import Language.Haskell.GhcMod.GhcPkg-import Language.Haskell.GhcMod.Monad (GhcModT)-import Language.Haskell.GhcMod.Target (setTargetFiles)-import Language.Haskell.GhcMod.Types--import Control.Applicative ((<$>))-import Distribution.Package (InstalledPackageId(..))-import qualified Data.Map as M-import GHC (DynFlags(..))-import qualified GHC as G-import GhcMonad-import qualified Packages as G-import qualified Module as G-import qualified OccName as G--------------------------------------------------------------------- get Packages,Modules,Bindings--ghcPkgDb :: GhcMonad m => m PkgDb-ghcPkgDb = M.fromList <$>- maybe [] (map toKv . filterInternal) <$> pkgDatabase <$> G.getSessionDynFlags- where- toKv pkg = (fromInstalledPackageId $ G.installedPackageId pkg, pkg)- filterInternal =- filter ((/= InstalledPackageId "builtin_rts") . G.installedPackageId)--package :: G.PackageConfig -> Package-package = fromInstalledPackageId . G.installedPackageId--modules :: G.PackageConfig -> [ModuleString]-modules = map G.moduleNameString . G.exposedModules--findModule :: ModuleString -> PkgDb -> [Package]-findModule m db = M.elems $ package `M.map` (containsModule `M.filter` db)- where- containsModule :: G.PackageConfig -> Bool- containsModule pkgConf =- G.mkModuleName m `elem` G.exposedModules pkgConf---ghcPkgId :: Package -> G.PackageId-ghcPkgId (name,_,_) =- -- TODO: Adding the package version too breaks 'findModule' for some reason- -- this isn't a big deal since in the common case where we're in a cabal- -- project we just use cabal's view of package dependencies anyways so we're- -- guaranteed to only have one version of each package exposed. However when- -- we're operating without a cabal project this will probaly cause trouble.- G.stringToPackageId name--type Binding = String---- | @moduleInfo mpkg module@. @mpkg@ should be 'Nothing' iff. moduleInfo--- should look for @module@ in the working directory.------ To map a 'ModuleString' to a package see 'findModule'-moduleInfo :: IOish m- => Maybe Package- -> ModuleString- -> GhcModT m (Maybe G.ModuleInfo)-moduleInfo mpkg mdl = do- let mdlName = G.mkModuleName mdl- mfsPkgId = G.packageIdFS . ghcPkgId <$> mpkg- loadLocalModule- G.findModule mdlName mfsPkgId >>= G.getModuleInfo- where- loadLocalModule = case mpkg of- Just _ -> return ()- Nothing -> setTargetFiles [mdl]--localModuleInfo :: IOish m => ModuleString -> GhcModT m (Maybe G.ModuleInfo)-localModuleInfo mdl = moduleInfo Nothing mdl--bindings :: G.ModuleInfo -> [Binding]-bindings minfo = map (G.occNameString . G.getOccName) $ G.modInfoExports minfo
− Language/Haskell/GhcMod/GHCChoice.hs
@@ -1,23 +0,0 @@-{-# LANGUAGE ScopedTypeVariables #-}--module Language.Haskell.GhcMod.GHCChoice where--import Control.Exception (IOException)-import CoreMonad (liftIO)-import qualified Exception as GE-import GHC (GhcMonad)---------------------------------------------------------------------- | Try the left 'Ghc' action. If 'IOException' occurs, try--- the right 'Ghc' action.-(||>) :: GhcMonad m => m a -> m a -> m a-x ||> y = x `GE.gcatch` (\(_ :: IOException) -> y)---- | Go to the next 'Ghc' monad by throwing 'AltGhcgoNext'.-goNext :: GhcMonad m => m a-goNext = liftIO . GE.throwIO $ userError "goNext"---- | Run any one 'Ghc' monad.-runAnyOne :: GhcMonad m => [m a] -> m a-runAnyOne = foldr (||>) goNext
− Language/Haskell/GhcMod/Gap.hs
@@ -1,475 +0,0 @@-{-# LANGUAGE TypeSynonymInstances, FlexibleInstances, CPP, ScopedTypeVariables, RankNTypes #-}--module Language.Haskell.GhcMod.Gap (- Language.Haskell.GhcMod.Gap.ClsInst- , mkTarget- , withStyle- , setLogAction- , getSrcSpan- , getSrcFile- , withContext- , fOptions- , toStringBuffer- , showSeverityCaption- , setCabalPkg- , setHideAllPackages- , addPackageFlags- , setDeferTypeErrors- , setWarnTypedHoles- , setDumpSplices- , isDumpSplices- , filterOutChildren- , infoThing- , pprInfo- , HasType(..)- , errorMsgSpan- , typeForUser- , nameForUser- , occNameForUser- , deSugar- , showDocWith- , GapThing(..)- , fromTyThing- , fileModSummary- , WarnFlags- , emptyWarnFlags- , benchmarkBuildInfo- , benchmarkTargets- , toModuleString- , GLMatch- , GLMatchI- , getClass- , occName- , setFlags- ) where--import Control.Applicative hiding (empty)-import Control.Monad (filterM)-import CoreSyn (CoreExpr)-import Data.List (intersperse)-import Data.Maybe (catMaybes)-import Data.Time.Clock (UTCTime)-import DataCon (dataConRepType)-import Desugar (deSugarExpr)-import DynFlags-import ErrUtils-import FastString-import GhcMonad-import HscTypes-import Language.Haskell.GhcMod.GHCChoice-import Language.Haskell.GhcMod.Types-import NameSet-import OccName-import Outputable-import PprTyThing-import StringBuffer-import TcType-import Var (varType)--import qualified Distribution.PackageDescription as P-import qualified InstEnv-import qualified Pretty-import qualified StringBuffer as SB--#if __GLASGOW_HASKELL__ >= 708-import FamInstEnv-import ConLike (ConLike(..))-import PatSyn (patSynType)-#else-import TcRnTypes-#endif--#if __GLASGOW_HASKELL__ >= 706-import GHC hiding (ClsInst)-#else-import GHC hiding (Instance)-import Control.Arrow hiding ((<+>))-import Data.Convertible-import RdrName (rdrNameOcc)-#endif--#if __GLASGOW_HASKELL__ >= 704-import qualified Data.IntSet as I (IntSet, empty)-import qualified Distribution.ModuleName as M (ModuleName,toFilePath)-#endif---------------------------------------------------------------------------------------------------------------------------------------#if __GLASGOW_HASKELL__ >= 706-type ClsInst = InstEnv.ClsInst-#else-type ClsInst = InstEnv.Instance-#endif--mkTarget :: TargetId -> Bool -> Maybe (SB.StringBuffer, UTCTime) -> Target-#if __GLASGOW_HASKELL__ >= 706-mkTarget = Target-#else-mkTarget tid allowObjCode = Target tid allowObjCode . (fmap . second) convert-#endif-------------------------------------------------------------------------------------------------------------------------------------withStyle :: DynFlags -> PprStyle -> SDoc -> Pretty.Doc-#if __GLASGOW_HASKELL__ >= 706-withStyle = withPprStyleDoc-#else-withStyle _ = withPprStyleDoc-#endif--setLogAction :: DynFlags- -> (DynFlags -> Severity -> SrcSpan -> PprStyle -> SDoc -> IO ())- -> DynFlags-setLogAction df f =-#if __GLASGOW_HASKELL__ >= 706- df { log_action = f }-#else- df { log_action = f df }-#endif--showDocWith :: DynFlags -> Pretty.Mode -> Pretty.Doc -> String-#if __GLASGOW_HASKELL__ >= 708--- Pretty.showDocWith disappeard.--- https://github.com/ghc/ghc/commit/08a3536e4246e323fbcd8040e0b80001950fe9bc-showDocWith dflags mode = Pretty.showDoc mode (pprCols dflags)-#else-showDocWith _ = Pretty.showDocWith-#endif-------------------------------------------------------------------------------------------------------------------------------------getSrcSpan :: SrcSpan -> Maybe (Int,Int,Int,Int)-#if __GLASGOW_HASKELL__ >= 702-getSrcSpan (RealSrcSpan spn)-#else-getSrcSpan spn | isGoodSrcSpan spn-#endif- = Just (srcSpanStartLine spn- , srcSpanStartCol spn- , srcSpanEndLine spn- , srcSpanEndCol spn)-getSrcSpan _ = Nothing--getSrcFile :: SrcSpan -> Maybe String-#if __GLASGOW_HASKELL__ >= 702-getSrcFile (RealSrcSpan spn) = Just . unpackFS . srcSpanFile $ spn-#else-getSrcFile spn | isGoodSrcSpan spn = Just . unpackFS . srcSpanFile $ spn-#endif-getSrcFile _ = Nothing--------------------------------------------------------------------toStringBuffer :: GhcMonad m => [String] -> m StringBuffer-#if __GLASGOW_HASKELL__ >= 702-toStringBuffer = return . stringToStringBuffer . unlines-#else-toStringBuffer = liftIO . stringToStringBuffer . unlines-#endif--------------------------------------------------------------------fOptions :: [String]-#if __GLASGOW_HASKELL__ >= 704-fOptions = [option | (option,_,_) <- fFlags]- ++ [option | (option,_,_) <- fWarningFlags]- ++ [option | (option,_,_) <- fLangFlags]-#else-fOptions = [option | (option,_,_,_) <- fFlags]- ++ [option | (option,_,_,_) <- fWarningFlags]- ++ [option | (option,_,_,_) <- fLangFlags]-#endif-------------------------------------------------------------------------------------------------------------------------------------fileModSummary :: GhcMonad m => FilePath -> m ModSummary-fileModSummary file = do- mss <- getModuleGraph- let [ms] = filter (\m -> ml_hs_file (ms_location m) == Just file) mss- return ms--withContext :: GhcMonad m => m a -> m a-withContext action = gbracket setup teardown body- where- setup = getContext- teardown = setCtx- body _ = do- topImports >>= setCtx- action- topImports = do- mss <- getModuleGraph- ms <- map modName <$> filterM isTop mss-#if __GLASGOW_HASKELL__ >= 704- return ms-#else- return (ms,[])-#endif- isTop mos = lookupMod mos ||> returnFalse- lookupMod mos = lookupModule (ms_mod_name mos) Nothing >> return True- returnFalse = return False-#if __GLASGOW_HASKELL__ >= 706- modName = IIModule . moduleName . ms_mod- setCtx = setContext-#elif __GLASGOW_HASKELL__ >= 704- modName = IIModule . ms_mod- setCtx = setContext-#else- modName = ms_mod- setCtx = uncurry setContext-#endif--showSeverityCaption :: Severity -> String-#if __GLASGOW_HASKELL__ >= 706-showSeverityCaption SevWarning = "Warning: "-showSeverityCaption _ = ""-#else-showSeverityCaption = const ""-#endif-------------------------------------------------------------------------------------------------------------------------------------setCabalPkg :: DynFlags -> DynFlags-#if __GLASGOW_HASKELL__ >= 708-setCabalPkg dflag = gopt_set dflag Opt_BuildingCabalPackage-#else-setCabalPkg dflag = dopt_set dflag Opt_BuildingCabalPackage-#endif--------------------------------------------------------------------setHideAllPackages :: DynFlags -> DynFlags-#if __GLASGOW_HASKELL__ >= 708-setHideAllPackages df = gopt_set df Opt_HideAllPackages-#else-setHideAllPackages df = dopt_set df Opt_HideAllPackages-#endif--addPackageFlags :: [Package] -> DynFlags -> DynFlags-addPackageFlags pkgs df =- df { packageFlags = packageFlags df ++ expose `map` pkgs }- where- expose pkg = ExposePackageId $ showPkgId pkg--------------------------------------------------------------------setDumpSplices :: DynFlags -> DynFlags-setDumpSplices dflag = dopt_set dflag Opt_D_dump_splices--isDumpSplices :: DynFlags -> Bool-isDumpSplices dflag = dopt Opt_D_dump_splices dflag---------------------------------------------------------------------setDeferTypeErrors :: DynFlags -> DynFlags-#if __GLASGOW_HASKELL__ >= 708-setDeferTypeErrors dflag = gopt_set dflag Opt_DeferTypeErrors-#elif __GLASGOW_HASKELL__ >= 706-setDeferTypeErrors dflag = dopt_set dflag Opt_DeferTypeErrors-#else-setDeferTypeErrors = id-#endif--setWarnTypedHoles :: DynFlags -> DynFlags-#if __GLASGOW_HASKELL__ >= 708-setWarnTypedHoles dflag = wopt_set dflag Opt_WarnTypedHoles-#else-setWarnTypedHoles = id-#endif-------------------------------------------------------------------------------------------------------------------------------------class HasType a where- getType :: GhcMonad m => TypecheckedModule -> a -> m (Maybe (SrcSpan, Type))---instance HasType (LHsBind Id) where-#if __GLASGOW_HASKELL__ >= 708- getType _ (L spn FunBind{fun_matches = m}) = return $ Just (spn, typ)- where in_tys = mg_arg_tys m- out_typ = mg_res_ty m- typ = mkFunTys in_tys out_typ-#else- getType _ (L spn FunBind{fun_matches = MatchGroup _ typ}) = return $ Just (spn, typ)-#endif- getType _ _ = return Nothing-------------------------------------------------------------------------------------------------------------------------------------- from ghc/InteractiveUI.hs--filterOutChildren :: (a -> TyThing) -> [a] -> [a]-filterOutChildren get_thing xs- = [x | x <- xs, not (getName (get_thing x) `elemNameSet` implicits)]- where- implicits = mkNameSet [getName t | x <- xs, t <- implicitTyThings (get_thing x)]--infoThing :: GhcMonad m => String -> m SDoc-infoThing str = do- names <- parseName str-#if __GLASGOW_HASKELL__ >= 708- mb_stuffs <- mapM (getInfo False) names- let filtered = filterOutChildren (\(t,_f,_i,_fam) -> t) (catMaybes mb_stuffs)-#else- mb_stuffs <- mapM getInfo names- let filtered = filterOutChildren (\(t,_f,_i) -> t) (catMaybes mb_stuffs)-#endif- return $ vcat (intersperse (text "") $ map (pprInfo False) filtered)--#if __GLASGOW_HASKELL__ >= 708-pprInfo :: Bool -> (TyThing, GHC.Fixity, [ClsInst], [FamInst]) -> SDoc-pprInfo _ (thing, fixity, insts, famInsts)- = pprTyThingInContextLoc thing- $$ show_fixity fixity- $$ InstEnv.pprInstances insts- $$ pprFamInsts famInsts- where- show_fixity fx- | fx == defaultFixity = Outputable.empty- | otherwise = ppr fx <+> ppr (getName thing)-#else-pprInfo :: PrintExplicitForalls -> (TyThing, GHC.Fixity, [ClsInst]) -> SDoc-pprInfo pefas (thing, fixity, insts)- = pprTyThingInContextLoc pefas thing- $$ show_fixity fixity- $$ vcat (map pprInstance insts)- where- show_fixity fx- | fx == defaultFixity = Outputable.empty- | otherwise = ppr fx <+> ppr (getName thing)-#endif-------------------------------------------------------------------------------------------------------------------------------------errorMsgSpan :: ErrMsg -> SrcSpan-#if __GLASGOW_HASKELL__ >= 708-errorMsgSpan = errMsgSpan-#else-errorMsgSpan = head . errMsgSpans-#endif--typeForUser :: Type -> SDoc-#if __GLASGOW_HASKELL__ >= 708-typeForUser = pprTypeForUser-#else-typeForUser = pprTypeForUser False-#endif--nameForUser :: Name -> SDoc-nameForUser = pprOccName . getOccName--occNameForUser :: OccName -> SDoc-occNameForUser = pprOccName--deSugar :: TypecheckedModule -> LHsExpr Id -> HscEnv- -> IO (Maybe CoreExpr)-#if __GLASGOW_HASKELL__ >= 708-deSugar _ e hs_env = snd <$> deSugarExpr hs_env e-#else-deSugar tcm e hs_env = snd <$> deSugarExpr hs_env modu rn_env ty_env e- where- modu = ms_mod $ pm_mod_summary $ tm_parsed_module tcm- tcgEnv = fst $ tm_internals_ tcm- rn_env = tcg_rdr_env tcgEnv- ty_env = tcg_type_env tcgEnv-#endif-------------------------------------------------------------------------------------------------------------------------------------data GapThing = GtA Type | GtT TyCon | GtN--fromTyThing :: TyThing -> GapThing-fromTyThing (AnId i) = GtA $ varType i-#if __GLASGOW_HASKELL__ >= 708-fromTyThing (AConLike (RealDataCon d)) = GtA $ dataConRepType d-fromTyThing (AConLike (PatSynCon p)) = GtA $ patSynType p-#else-fromTyThing (ADataCon d) = GtA $ dataConRepType d-#endif-fromTyThing (ATyCon t) = GtT t-fromTyThing _ = GtN-------------------------------------------------------------------------------------------------------------------------------------#if __GLASGOW_HASKELL__ >= 704-type WarnFlags = I.IntSet-emptyWarnFlags :: WarnFlags-emptyWarnFlags = I.empty-#else-type WarnFlags = [WarningFlag]-emptyWarnFlags :: WarnFlags-emptyWarnFlags = []-#endif-------------------------------------------------------------------------------------------------------------------------------------benchmarkBuildInfo :: P.PackageDescription -> [P.BuildInfo]-#if __GLASGOW_HASKELL__ >= 704-benchmarkBuildInfo pd = map P.benchmarkBuildInfo $ P.benchmarks pd-#else-benchmarkBuildInfo pd = []-#endif--benchmarkTargets :: P.PackageDescription -> [String]-#if __GLASGOW_HASKELL__ >= 704-benchmarkTargets pd = map toModuleString $ concatMap P.benchmarkModules $ P.benchmarks pd-#else-benchmarkTargets = []-#endif--toModuleString :: M.ModuleName -> String-toModuleString mn = fromFilePath $ M.toFilePath mn- where- fromFilePath :: FilePath -> String- fromFilePath fp = map (\c -> if c=='/' then '.' else c) fp-------------------------------------------------------------------------------------------------------------------------------------#if __GLASGOW_HASKELL__ >= 708-type GLMatch = LMatch RdrName (LHsExpr RdrName)-type GLMatchI = LMatch Id (LHsExpr Id)-#else-type GLMatch = LMatch RdrName-type GLMatchI = LMatch Id-#endif--getClass :: [LInstDecl Name] -> Maybe (Name, SrcSpan)-#if __GLASGOW_HASKELL__ >= 708--- Instance declarations of sort 'instance F (G a)'-getClass [L loc (ClsInstD (ClsInstDecl {cid_poly_ty = (L _ (HsForAllTy _ _ _ (L _ (HsAppTy (L _ (HsTyVar className)) _))))}))] = Just (className, loc)--- Instance declarations of sort 'instance F G' (no variables)-getClass [L loc (ClsInstD (ClsInstDecl {cid_poly_ty = (L _ (HsAppTy (L _ (HsTyVar className)) _))}))] = Just (className, loc)-#elif __GLASGOW_HASKELL__ >= 706-getClass [L loc (ClsInstD (L _ (HsForAllTy _ _ _ (L _ (HsAppTy (L _ (HsTyVar className)) _)))) _ _ _)] = Just (className, loc)-getClass[L loc (ClsInstD (L _ (HsAppTy (L _ (HsTyVar className)) _)) _ _ _)] = Just (className, loc)-#else-getClass [L loc (InstDecl (L _ (HsForAllTy _ _ _ (L _ (HsAppTy (L _ (HsTyVar className)) _)))) _ _ _)] = Just (className, loc)-getClass [L loc (InstDecl (L _ (HsAppTy (L _ (HsTyVar className)) _)) _ _ _)] = Just (className, loc)-#endif-getClass _ = Nothing--#if __GLASGOW_HASKELL__ < 706-occName :: RdrName -> OccName-occName = rdrNameOcc-#endif-------------------------------------------------------------------------------------------------------------------------------------setFlags :: DynFlags -> DynFlags-#if __GLASGOW_HASKELL__ >= 708-setFlags df = df `gopt_unset` Opt_SpecConstr -- consume memory if -O2-#else-setFlags = id-#endif
− Language/Haskell/GhcMod/GhcPkg.hs
@@ -1,97 +0,0 @@-{-# LANGUAGE BangPatterns, ScopedTypeVariables, TupleSections #-}-module Language.Haskell.GhcMod.GhcPkg (- ghcPkgDbOpt- , ghcPkgDbStackOpts- , ghcDbStackOpts- , ghcDbOpt- , fromInstalledPackageId- , fromInstalledPackageId'- , getSandboxDb- , getPackageDbStack- ) where--import Config (cProjectVersionInt)-import Control.Applicative ((<$>))-import Control.Exception (SomeException(..))-import qualified Control.Exception as E-import Data.Char (isSpace)-import Data.List (isPrefixOf, intercalate)-import Data.List.Split (splitOn)-import Distribution.Package (InstalledPackageId(..))-import Language.Haskell.GhcMod.Types-import Language.Haskell.GhcMod.Utils-import System.FilePath ((</>))--ghcVersion :: Int-ghcVersion = read cProjectVersionInt---- | Get path to sandbox package db-getSandboxDb :: FilePath -- ^ Path to the cabal package root directory- -- (containing the @cabal.sandbox.config@ file)- -> IO FilePath-getSandboxDb cdir = getSandboxDbDir (cdir </> "cabal.sandbox.config")---- | Extract the sandbox package db directory from the cabal.sandbox.config file.--- Exception is thrown if the sandbox config file is broken.-getSandboxDbDir :: FilePath -- ^ Path to the @cabal.sandbox.config@ file- -> IO FilePath-getSandboxDbDir sconf = do- -- Be strict to ensure that an error can be caught.- !path <- extractValue . parse <$> readFile sconf- return path- where- key = "package-db:"- keyLen = length key-- parse = head . filter (key `isPrefixOf`) . lines- extractValue = dropWhileEnd isSpace . dropWhile isSpace . drop keyLen--getPackageDbStack :: FilePath -- ^ Project Directory (where the- -- cabal.sandbox.config file would be if it- -- exists)- -> IO [GhcPkgDb]-getPackageDbStack cdir =- (getSandboxDb cdir >>= \db -> return [GlobalDb, PackageDb db])- `E.catch` \(_ :: SomeException) -> return [GlobalDb, UserDb]--fromInstalledPackageId' :: InstalledPackageId -> Maybe Package-fromInstalledPackageId' pid = let- InstalledPackageId pkg = pid- in case reverse $ splitOn "-" pkg of- i:v:rest -> Just (intercalate "-" (reverse rest), v, i)- _ -> Nothing--fromInstalledPackageId :: InstalledPackageId -> Package-fromInstalledPackageId pid =- case fromInstalledPackageId' pid of- Just p -> p- Nothing -> error $- "fromInstalledPackageId: `"++show pid++"' is not a valid package-id"---- | Get options needed to add a list of package dbs to ghc-pkg's db stack-ghcPkgDbStackOpts :: [GhcPkgDb] -- ^ Package db stack- -> [String]-ghcPkgDbStackOpts dbs = ghcPkgDbOpt `concatMap` dbs---- | Get options needed to add a list of package dbs to ghc's db stack-ghcDbStackOpts :: [GhcPkgDb] -- ^ Package db stack- -> [String]-ghcDbStackOpts dbs = ghcDbOpt `concatMap` dbs--ghcPkgDbOpt :: GhcPkgDb -> [String]-ghcPkgDbOpt GlobalDb = ["--global"]-ghcPkgDbOpt UserDb = ["--user"]-ghcPkgDbOpt (PackageDb pkgDb)- | ghcVersion < 706 = ["--no-user-package-conf", "--package-conf=" ++ pkgDb]- | otherwise = ["--no-user-package-db", "--package-db=" ++ pkgDb]--ghcDbOpt :: GhcPkgDb -> [String]-ghcDbOpt GlobalDb- | ghcVersion < 706 = ["-global-package-conf"]- | otherwise = ["-global-package-db"]-ghcDbOpt UserDb- | ghcVersion < 706 = ["-user-package-conf"]- | otherwise = ["-user-package-db"]-ghcDbOpt (PackageDb pkgDb)- | ghcVersion < 706 = ["-no-user-package-conf", "-package-conf", pkgDb]- | otherwise = ["-no-user-package-db", "-package-db", pkgDb]
− Language/Haskell/GhcMod/Info.hs
@@ -1,65 +0,0 @@-module Language.Haskell.GhcMod.Info (- info- , types- ) where--import Control.Applicative ((<$>))-import Data.Function (on)-import Data.List (sortBy)-import Data.Maybe (catMaybes)-import Exception (ghandle, SomeException(..))-import GHC (GhcMonad, LHsBind, LHsExpr, LPat, Id, TypecheckedModule(..), SrcSpan, Type)-import qualified GHC as G-import Language.Haskell.GhcMod.Doc (showPage)-import Language.Haskell.GhcMod.Gap (HasType(..))-import qualified Language.Haskell.GhcMod.Gap as Gap-import Language.Haskell.GhcMod.Monad-import Language.Haskell.GhcMod.SrcUtils-import Language.Haskell.GhcMod.Types-import Language.Haskell.GhcMod.Convert---------------------------------------------------------------------- | Obtaining information of a target expression. (GHCi's info:)-info :: IOish m- => FilePath -- ^ A target file.- -> Expression -- ^ A Haskell expression.- -> GhcModT m String-info file expr = do- opt <- options- convert opt <$> ghandle handler body- where- body = inModuleContext file $ \dflag style -> do- sdoc <- Gap.infoThing expr- return $ showPage dflag style sdoc- handler (SomeException _) = return "Cannot show info"---------------------------------------------------------------------- | Obtaining type of a target expression. (GHCi's type:)-types :: IOish m- => FilePath -- ^ A target file.- -> Int -- ^ Line number.- -> Int -- ^ Column number.- -> GhcModT m String-types file lineNo colNo = do- opt <- options- convert opt <$> ghandle handler body- where- body = inModuleContext file $ \dflag style -> do- modSum <- Gap.fileModSummary file- srcSpanTypes <- getSrcSpanType modSum lineNo colNo- return $ map (toTup dflag style) $ sortBy (cmp `on` fst) srcSpanTypes- handler (SomeException _) = return []--getSrcSpanType :: GhcMonad m => G.ModSummary -> Int -> Int -> m [(SrcSpan, Type)]-getSrcSpanType modSum lineNo colNo = do- p <- G.parseModule modSum- tcm@TypecheckedModule{tm_typechecked_source = tcs} <- G.typecheckModule p- let bs = listifySpans tcs (lineNo, colNo) :: [LHsBind Id]- es = listifySpans tcs (lineNo, colNo) :: [LHsExpr Id]- ps = listifySpans tcs (lineNo, colNo) :: [LPat Id]- bts <- mapM (getType tcm) bs- ets <- mapM (getType tcm) es- pts <- mapM (getType tcm) ps- return $ catMaybes $ concat [ets, bts, pts]
− Language/Haskell/GhcMod/Internal.hs
@@ -1,63 +0,0 @@--- | Low level access to the ghc-mod library.--module Language.Haskell.GhcMod.Internal (- -- * Types- GHCOption- , Package- , PackageBaseName- , PackageVersion- , PackageId- , IncludeDir- , CompilerOptions(..)- -- * Cabal API- , parseCabalFile- , getCompilerOptions- , cabalAllBuildInfo- , cabalDependPackages- , cabalSourceDirs- , cabalAllTargets- -- * GHC.Paths- , ghcLibDir- -- * IO- , getDynamicFlags- -- * Targets- , setTargetFiles- -- * Logging- , withLogger- , setNoWarningFlags- , setAllWarningFlags- -- * Environment, state and logging- , GhcModEnv(..)- , newGhcModEnv- , GhcModState- , defaultState- , CompilerMode(..)- , GhcModLog- -- * Monad utilities- , runGhcModT'- , hoistGhcModT- -- ** Accessing 'GhcModEnv' and 'GhcModState'- , options- , cradle- , getCompilerMode- , setCompilerMode- , withOptions- -- * 'GhcMonad' Choice- , (||>)- , goNext- , runAnyOne- ) where--import GHC.Paths (libdir)--import Language.Haskell.GhcMod.CabalApi-import Language.Haskell.GhcMod.DynFlags-import Language.Haskell.GhcMod.GHCChoice-import Language.Haskell.GhcMod.Logger-import Language.Haskell.GhcMod.Monad-import Language.Haskell.GhcMod.Target-import Language.Haskell.GhcMod.Types---- | Obtaining the directory for ghc system libraries.-ghcLibDir :: FilePath-ghcLibDir = libdir
− Language/Haskell/GhcMod/Lang.hs
@@ -1,10 +0,0 @@-module Language.Haskell.GhcMod.Lang where--import DynFlags (supportedLanguagesAndExtensions)-import Language.Haskell.GhcMod.Convert-import Language.Haskell.GhcMod.Monad---- | Listing language extensions.--languages :: IOish m => GhcModT m String-languages = convert' supportedLanguagesAndExtensions
− Language/Haskell/GhcMod/Lint.hs
@@ -1,21 +0,0 @@-module Language.Haskell.GhcMod.Lint where--import Exception (ghandle)-import Control.Exception (SomeException(..))-import Language.Haskell.GhcMod.Logger (checkErrorPrefix)-import Language.Haskell.GhcMod.Convert-import Language.Haskell.GhcMod.Monad-import Language.Haskell.GhcMod.Types-import Language.Haskell.HLint (hlint)---- | Checking syntax of a target file using hlint.--- Warnings and errors are returned.-lint :: IOish m- => FilePath -- ^ A target file.- -> GhcModT m String-lint file = do- opt <- options- ghandle handler . pack =<< liftIO (hlint $ file : "--quiet" : hlintOpts opt)- where- pack = convert' . map (init . show) -- init drops the last \n.- handler (SomeException e) = return $ checkErrorPrefix ++ show e ++ "\n"
− Language/Haskell/GhcMod/Logger.hs
@@ -1,232 +0,0 @@-{-# LANGUAGE CPP #-}--module Language.Haskell.GhcMod.Logger (- withLogger- , withLoggerTwice- , checkErrorPrefix- ) where--import Bag (Bag, bagToList, emptyBag, consBag, filterBag, unionBags)-import Control.Applicative ((<$>))-import Data.IORef (IORef, newIORef, readIORef, writeIORef, modifyIORef)-import Data.List (isPrefixOf, find, nub, isInfixOf)-import Data.Maybe (fromMaybe, isJust)-import ErrUtils (ErrMsg, WarnMsg, errMsgShortDoc, errMsgExtraInfo, mkWarnMsg)-import Exception (ghandle)-import GHC (DynFlags, SrcSpan, Severity(SevError))-import qualified GHC as G-import HscTypes (SourceError, srcErrorMessages)-import Language.Haskell.GhcMod.Doc (showPage, getStyle)-import Language.Haskell.GhcMod.DynFlags (withDynFlags, withCmdFlags)-import qualified Language.Haskell.GhcMod.Gap as Gap-import Language.Haskell.GhcMod.Convert (convert')-import Language.Haskell.GhcMod.Monad-import Language.Haskell.GhcMod.Types-import Outputable (PprStyle, SDoc, qualName, qualModule, mkErrStyle, neverQualify)-import System.FilePath (normalise)--------------------------------------------------------------------type Builder = [String] -> [String]--data Log = Log [String] Builder--newtype LogRef = LogRef (IORef Log)--emptyLog :: Log-emptyLog = Log [] id--newLogRef :: IO LogRef-newLogRef = LogRef <$> newIORef emptyLog--readAndClearLogRef :: IOish m => LogRef -> GhcModT m String-readAndClearLogRef (LogRef ref) = do- Log _ b <- liftIO $ readIORef ref- liftIO $ writeIORef ref emptyLog- convert' (b [])--appendLogRef :: DynFlags -> LogRef -> DynFlags -> Severity -> SrcSpan -> PprStyle -> SDoc -> IO ()-appendLogRef df (LogRef ref) _ sev src style msg = modifyIORef ref update- where- l = ppMsg src sev df style msg- update lg@(Log ls b)- | l `elem` ls = lg- | otherwise = Log (l:ls) (b . (l:))--------------------------------------------------------------------data LogBag = LogBag (Bag WarnMsg)-newtype LogBagRef = LogBagRef (IORef LogBag)--emptyLogBag :: LogBag-emptyLogBag = LogBag emptyBag--newLogBagRef :: IO LogBagRef-newLogBagRef = LogBagRef <$> newIORef emptyLogBag--readAndClearLogBagRef :: IOish m => LogBagRef -> GhcModT m (Bag WarnMsg)-readAndClearLogBagRef (LogBagRef ref) = do- LogBag b <- liftIO $ readIORef ref- liftIO $ writeIORef ref emptyLogBag- return b--appendLogBagRef :: DynFlags -> LogBagRef -> DynFlags -> Severity -> SrcSpan -> PprStyle -> SDoc -> IO ()-appendLogBagRef df (LogBagRef ref) _ _ src style msg = modifyIORef ref update- where- qstyle = (qualName style, qualModule style)-#if __GLASGOW_HASKELL__ >= 706- warnMsg = mkWarnMsg df src qstyle msg-#else- warnMsg = mkWarnMsg src qstyle msg-#endif- warnBag = consBag warnMsg emptyBag- update (LogBag b) = let (b1,b2) = mergeErrors df style b warnBag- in LogBag $ b1 `unionBags` b2---------------------------------------------------------------------- | Set the session flag (e.g. "-Wall" or "-w:") then--- executes a body. Logged messages are returned as 'String'.--- Right is success and Left is failure.-withLogger :: IOish m- => (DynFlags -> DynFlags)- -> GhcModT m ()- -> GhcModT m (Either String String)-withLogger setDF body = ghandle sourceError $ do- logref <- liftIO newLogRef- wflags <- filter ("-fno-warn" `isPrefixOf`) . ghcUserOptions <$> options- withDynFlags (setLogger logref . setDF) $- withCmdFlags wflags $ do- body- Right <$> readAndClearLogRef logref- where- setLogger logref df = Gap.setLogAction df $ appendLogRef df logref--withLoggerTwice :: IOish m- => (DynFlags -> DynFlags)- -> GhcModT m ()- -> (DynFlags -> DynFlags)- -> GhcModT m ()- -> GhcModT m (Either String String)-withLoggerTwice setDF1 body1 setDF2 body2 = do- err1 <- ghandle sourceErrorBag $ do- logref <- liftIO newLogBagRef- wflags <- filter ("-fno-warn" `isPrefixOf`) . ghcUserOptions <$> options- withDynFlags (setLogger logref . setDF1) $- withCmdFlags wflags $ do- body1- Right <$> readAndClearLogBagRef logref- err2 <- ghandle sourceErrorBag $ do- logref <- liftIO newLogBagRef- wflags <- filter ("-fno-warn" `isPrefixOf`) . ghcUserOptions <$> options- withDynFlags (setLogger logref . setDF2) $- withCmdFlags wflags $ do- body2- Right <$> readAndClearLogBagRef logref- -- Merge errors and warnings- dflags <- G.getSessionDynFlags- style <- getStyle- case (err1, err2) of- (Right b1, Right b2) -> do let (warn1,_) = mergeErrors dflags style b1 b2- errAndWarnBagToStr Right emptyBag (warn1 `unionBags` b2)- (Left b1, Right b2) -> do let (err,warn) = mergeErrors dflags style b1 b2- errAndWarnBagToStr Right err warn- (Right b1, Left b2) -> do let (err,warn) = mergeErrors dflags style b2 b1- errAndWarnBagToStr Right err warn- (Left b1, Left b2) -> do let (err1',err2') = mergeErrors dflags style b1 b2- errAndWarnBagToStr Right (err1' `unionBags` err2') emptyBag- where- setLogger logref df = Gap.setLogAction df $ appendLogBagRef df logref---------------------------------------------------------------------- | Converting 'SourceError' to 'String'.-sourceError :: IOish m => SourceError -> GhcModT m (Either String String)-sourceError err = errBagToStr (srcErrorMessages err)--errBagToStr :: IOish m => Bag ErrMsg -> GhcModT m (Either String String)-errBagToStr = errBagToStr' Left--errBagToStr' :: IOish m => (String -> a) -> Bag ErrMsg -> GhcModT m a-errBagToStr' f err = do- dflags <- G.getSessionDynFlags- style <- getStyle- ret <- convert' (errBagToStrList dflags style err)- return $ f ret--errAndWarnBagToStr :: IOish m => (String -> a) -> Bag ErrMsg -> Bag WarnMsg -> GhcModT m a-errAndWarnBagToStr f err warn = do- dflags <- G.getSessionDynFlags- -- style <- toGhcModT getStyle-#if __GLASGOW_HASKELL__ >= 706- let style = mkErrStyle dflags neverQualify-#else- let style = mkErrStyle neverQualify-#endif- ret <- convert' $ nub (errBagToStrList dflags style err ++ warnBagToStrList dflags style warn)- return $ f ret--errBagToStrList :: DynFlags -> PprStyle -> Bag ErrMsg -> [String]-errBagToStrList dflag style = map (ppErrMsg dflag style) . reverse . bagToList--warnBagToStrList :: DynFlags -> PprStyle -> Bag WarnMsg -> [String]-warnBagToStrList dflag style = map (ppWarnMsg dflag style) . reverse . bagToList--sourceErrorBag :: IOish m => SourceError -> GhcModT m (Either (Bag ErrMsg) (Bag WarnMsg))-sourceErrorBag err = return $ Left (srcErrorMessages err)--mergeErrors :: DynFlags -> PprStyle -> Bag ErrMsg -> Bag ErrMsg -> (Bag ErrMsg, Bag ErrMsg)-mergeErrors dflag style b1 b2 =- let b1Msgs = map (\err1 -> let m = ppWarnMsg dflag style err1 in (m, head $ lines m))- (bagToList b1)- mustBeB2 = \err2 -> let msg2 = ppWarnMsg dflag style err2- line2 = head $ lines msg2- in not . isJust $ find (\(msg1, line1) -> msg1 == msg2 || (line1 == line2 && isHoleMsg line1)) b1Msgs- in (b1, filterBag mustBeB2 b2)--isHoleMsg :: String -> Bool-isHoleMsg = isInfixOf "Found hole"--------------------------------------------------------------------ppErrMsg :: DynFlags -> PprStyle -> ErrMsg -> String-ppErrMsg dflag style err = ppMsg spn SevError dflag style msg ++ (if null ext then "" else "\n" ++ ext)- where- spn = Gap.errorMsgSpan err- msg = errMsgShortDoc err- ext = showPage dflag style (errMsgExtraInfo err)--ppWarnMsg :: DynFlags -> PprStyle -> ErrMsg -> String-ppWarnMsg dflag style err = ppMsg spn G.SevWarning dflag style msg ++ (if null ext then "" else "\n" ++ ext)- where- spn = Gap.errorMsgSpan err- msg = errMsgShortDoc err- ext = showPage dflag style (errMsgExtraInfo err)--ppMsg :: SrcSpan -> Severity-> DynFlags -> PprStyle -> SDoc -> String-ppMsg spn sev dflag style msg = prefix ++ cts- where- cts = showPage dflag style msg- prefix = ppMsgPrefix spn sev dflag style cts--ppMsgPrefix :: SrcSpan -> Severity-> DynFlags -> PprStyle -> String -> String-ppMsgPrefix spn sev dflag _style cts =- let defaultPrefix- | Gap.isDumpSplices dflag = ""- | otherwise = checkErrorPrefix- in fromMaybe defaultPrefix $ do- (line,col,_,_) <- Gap.getSrcSpan spn- file <- normalise <$> Gap.getSrcFile spn- let severityCaption = Gap.showSeverityCaption sev- pref0 | or (map (\x -> x `isPrefixOf` cts) warningAsErrorPrefixes)- = file ++ ":" ++ show line ++ ":" ++ show col ++ ":"- | otherwise = file ++ ":" ++ show line ++ ":" ++ show col ++ ":" ++ severityCaption- return pref0--checkErrorPrefix :: String-checkErrorPrefix = "Dummy:0:0:Error:"--warningAsErrorPrefixes :: [String]-warningAsErrorPrefixes = ["Couldn't match expected type"- , "Couldn't match type"- , "No instance for"]
− Language/Haskell/GhcMod/Modules.hs
@@ -1,32 +0,0 @@-module Language.Haskell.GhcMod.Modules (modules) where--import Control.Applicative ((<$>))-import Control.Exception (SomeException(..))-import Data.List (nub, sort)-import qualified GHC as G-import Language.Haskell.GhcMod.Convert-import Language.Haskell.GhcMod.Monad-import Language.Haskell.GhcMod.Types-import Packages (pkgIdMap, exposedModules, sourcePackageId, display)-import UniqFM (eltsUFM)---------------------------------------------------------------------- | Listing installed modules.-modules :: IOish m => GhcModT m String-modules = do- opt <- options- convert opt . arrange opt <$> (getModules `G.gcatch` handler)- where- getModules = getExposedModules <$> G.getSessionDynFlags- getExposedModules = concatMap exposedModules'- . eltsUFM . pkgIdMap . G.pkgState- exposedModules' p =- map G.moduleNameString (exposedModules p)- `zip`- repeat (display $ sourcePackageId p)- arrange opt = nub . sort . map (dropPkgs opt)- dropPkgs opt (name, pkg)- | detailed opt = name ++ " " ++ pkg- | otherwise = name- handler (SomeException _) = return []
− Language/Haskell/GhcMod/Monad.hs
@@ -1,432 +0,0 @@-{-# LANGUAGE CPP, GeneralizedNewtypeDeriving, FlexibleInstances #-}-{-# LANGUAGE FlexibleContexts, MultiParamTypeClasses, RankNTypes #-}-{-# LANGUAGE TypeFamilies, UndecidableInstances, RecordWildCards #-}-{-# OPTIONS_GHC -fno-warn-orphans #-}--module Language.Haskell.GhcMod.Monad (- -- * Monad Types- GhcModT- , IOish- -- ** Environment, state and logging- , GhcModEnv(..)- , newGhcModEnv- , GhcModState(..)- , defaultState- , CompilerMode(..)- , GhcModLog- , GhcModError(..)- -- * Monad utilities- , runGhcModT- , runGhcModT'- , hoistGhcModT- -- ** Accessing 'GhcModEnv' and 'GhcModState'- , gmsGet- , gmsPut- , options- , cradle- , getCompilerMode- , setCompilerMode- , withOptions- , withTempSession- , overrideGhcUserOptions- -- ** Re-exporting convenient stuff- , liftIO- , module Control.Monad.Reader.Class- , module Control.Monad.Journal.Class- ) where--#if __GLASGOW_HASKELL__ < 708--- 'CoreMonad.MonadIO' and 'Control.Monad.IO.Class.MonadIO' are different--- classes before ghc 7.8-#define DIFFERENT_MONADIO 1---- RWST doen't have a MonadIO instance before ghc 7.8-#define MONADIO_INSTANCES 1-#endif---import Language.Haskell.GhcMod.Types-import Language.Haskell.GhcMod.Error-import Language.Haskell.GhcMod.Cradle-import Language.Haskell.GhcMod.DynFlags-import Language.Haskell.GhcMod.GhcPkg-import Language.Haskell.GhcMod.CabalApi-import qualified Language.Haskell.GhcMod.Gap as Gap--import DynFlags-import GHC-import qualified GHC as G-import GHC.Paths (libdir)-import GhcMonad hiding (withTempSession)-#if __GLASGOW_HASKELL__ <= 702-import HscTypes-#endif---- MonadUtils of GHC 7.6 or earlier defines its own MonadIO.--- RWST does not automatically become an instance of MonadIO.--- MonadUtils of GHC 7.8 or later imports MonadIO in Monad.Control.IO.Class.--- So, RWST automatically becomes an instance of MonadIO.-import MonadUtils--#if DIFFERENT_MONADIO-import Control.Monad.Trans.Class (lift)-import qualified Control.Monad.IO.Class-import Data.Monoid (Monoid)-#endif--import Control.Applicative (Alternative)-import Control.Arrow (first)-import Control.Monad (MonadPlus, void, liftM)-import Control.Monad.Base (MonadBase, liftBase)---- Monad transformer stuff-import Control.Monad.Trans.Control (MonadBaseControl(..), StM, liftBaseWith,- control, liftBaseOp, liftBaseOp_)--import Control.Monad.Trans.Class-import Control.Monad.Reader.Class-import Control.Monad.Writer.Class (MonadWriter)-import Control.Monad.State.Class (MonadState(..))--import Control.Monad.Error (ErrorT, runErrorT)-import Control.Monad.Reader (ReaderT, runReaderT)-import Control.Monad.State.Strict (StateT, runStateT)-import Control.Monad.Trans.Journal (JournalT, runJournalT)-#ifdef MONADIO_INSTANCES-import Control.Monad.Trans.Maybe (MaybeT)-import Control.Monad.Error (Error(..))-#endif-import Control.Monad.Journal.Class--import Data.Maybe (fromJust, isJust)-import Data.IORef (IORef, readIORef, writeIORef, newIORef)-import System.Directory (getCurrentDirectory)-import System.IO.Error (tryIOError)--------------------------------------------------------------------data GhcModEnv = GhcModEnv {- gmGhcSession :: !(IORef HscEnv)- , gmOptions :: Options- , gmCradle :: Cradle- }--type GhcModLog = ()--data GhcModState = GhcModState {- gmCompilerMode :: CompilerMode- } deriving (Eq,Show,Read)--data CompilerMode = Simple | Intelligent deriving (Eq,Show,Read)--defaultState :: GhcModState-defaultState = GhcModState Simple---------------------------------------------------------------------- | This is basically a newtype wrapper around 'StateT', 'ErrorT', 'JournalT'--- and 'ReaderT' with custom instances for 'GhcMonad' and it's constraints that--- means you can run (almost) all functions from the GHC API on top of 'GhcModT'--- transparently.------ The inner monad @m@ should have instances for 'MonadIO' and--- 'MonadBaseControl' 'IO', in the common case this is simply 'IO'. Most @mtl@--- monads already have 'MonadBaseControl' 'IO' instances, see the--- @monad-control@ package.-newtype GhcModT m a = GhcModT {- unGhcModT :: StateT GhcModState- (ErrorT GhcModError- (JournalT GhcModLog- (ReaderT GhcModEnv m) ) ) a- } deriving ( Functor- , Applicative- , Alternative- , Monad- , MonadPlus-#if DIFFERENT_MONADIO- , Control.Monad.IO.Class.MonadIO-#endif- , MonadReader GhcModEnv -- TODO: make MonadReader instance- -- pass-through like MonadState- , MonadWriter w- , MonadError GhcModError- )--instance MonadIO m => MonadIO (GhcModT m) where- liftIO action = do- res <- GhcModT . liftIO . liftIO . liftIO . liftIO $ tryIOError action- case res of- Right a -> return a- Left e -> case show e of- "" -> throwError $ noMsg- msg -> throwError $ strMsg msg--instance MonadTrans (GhcModT) where- lift = GhcModT . lift . lift . lift . lift--instance MonadState s m => MonadState s (GhcModT m) where- get = GhcModT $ lift $ lift $ lift get- put = GhcModT . lift . lift . lift . put- state = GhcModT . lift . lift . lift . state---#if MONADIO_INSTANCES-instance MonadIO m => MonadIO (StateT s m) where- liftIO = lift . liftIO--instance MonadIO m => MonadIO (ReaderT r m) where- liftIO = lift . liftIO--instance (Monoid w, MonadIO m) => MonadIO (JournalT w m) where- liftIO = lift . liftIO--instance (Error e, MonadIO m) => MonadIO (ErrorT e m) where- liftIO = lift . liftIO--instance MonadIO m => MonadIO (MaybeT m) where- liftIO = lift . liftIO-#endif---------------------------------------------------------------------- | Initialize the 'DynFlags' relating to the compilation of a single--- file or GHC session according to the 'Cradle' and 'Options'--- provided.-initializeFlagsWithCradle :: (IOish m, GhcMonad m, MonadError GhcModError m)- => Options- -> Cradle- -> m ()-initializeFlagsWithCradle opt c- | cabal = withCabal- | otherwise = withSandbox- where- mCradleFile = cradleCabalFile c- cabal = isJust mCradleFile- ghcopts = ghcUserOptions opt- withCabal = do- pkgDesc <- parseCabalFile c $ fromJust mCradleFile- compOpts <- getCompilerOptions ghcopts c pkgDesc- initSession CabalPkg opt compOpts- withSandbox = initSession SingleFile opt compOpts- where- importDirs = [".","..","../..","../../..","../../../..","../../../../.."]- pkgOpts = ghcDbStackOpts $ cradlePkgDbStack c- compOpts- | null pkgOpts = CompilerOptions ghcopts importDirs []- | otherwise = CompilerOptions (ghcopts ++ pkgOpts) [wdir,rdir] []- wdir = cradleCurrentDir c- rdir = cradleRootDir c--initSession :: GhcMonad m- => Build- -> Options- -> CompilerOptions- -> m ()-initSession build Options {..} CompilerOptions {..} = do- df <- G.getSessionDynFlags- void $ G.setSessionDynFlags =<< addCmdOpts ghcOptions- ( setModeSimple- $ Gap.setFlags- $ setIncludeDirs includeDirs- $ setBuildEnv build- $ setEmptyLogger- $ Gap.addPackageFlags depPackages df)--------------------------------------------------------------------newGhcModEnv :: Options -> FilePath -> IO GhcModEnv-newGhcModEnv opt dir = do- session <- newIORef (error "empty session")- c <- findCradle' dir- return GhcModEnv {- gmGhcSession = session- , gmOptions = opt- , gmCradle = c- }---- | Run a @GhcModT m@ computation.-runGhcModT :: IOish m- => Options- -> GhcModT m a- -> m (Either GhcModError a, GhcModLog)-runGhcModT opt action = do- env <- liftBase $ newGhcModEnv opt =<< getCurrentDirectory- first (fst <$>) <$> (runGhcModT' env defaultState $ do- dflags <- getSessionDynFlags- defaultCleanupHandler dflags $ do- initializeFlagsWithCradle opt (gmCradle env)- action)---- | @hoistGhcModT result@. Embed a GhcModT computation's result into a GhcModT--- computation. Note that if the computation that returned @result@ modified the--- state part of GhcModT this cannot be restored.-hoistGhcModT :: IOish m- => (Either GhcModError a, GhcModLog)- -> GhcModT m a-hoistGhcModT (r,l) = do- GhcModT (lift $ lift $ journal l) >> case r of- Left e -> throwError e- Right a -> return a---- | Run a computation inside @GhcModT@ providing the RWST environment and--- initial state. This is a low level function, use it only if you know what to--- do with 'GhcModEnv' and 'GhcModState'.------ You should probably look at 'runGhcModT' instead.-runGhcModT' :: IOish m- => GhcModEnv- -> GhcModState- -> GhcModT m a- -> m (Either GhcModError (a, GhcModState), GhcModLog)-runGhcModT' r s a = do- (res, w') <-- flip runReaderT r $ runJournalT $ runErrorT $- runStateT (unGhcModT $ initGhcMonad (Just libdir) >> a) s- return (res, w')-------------------------------------------------------------------- | Make a copy of the 'gmGhcSession' IORef, run the action and restore the--- original 'HscEnv'.-withTempSession :: IOish m => GhcModT m a -> GhcModT m a-withTempSession action = do- session <- gmGhcSession <$> ask- savedHscEnv <- liftIO $ readIORef session- a <- action- liftIO $ writeIORef session savedHscEnv- return a---- | This is a very ugly workaround don't use it.-overrideGhcUserOptions :: IOish m => ([GHCOption] -> GhcModT m b) -> GhcModT m b-overrideGhcUserOptions action = withTempSession $ do- env <- ask- opt <- options- let ghcOpts = ghcUserOptions opt- opt' = opt { ghcUserOptions = [] }-- initializeFlagsWithCradle opt' (gmCradle env)-- action ghcOpts--------------------------------------------------------------------gmeAsk :: IOish m => GhcModT m GhcModEnv-gmeAsk = ask--gmsGet :: IOish m => GhcModT m GhcModState-gmsGet = GhcModT get--gmsPut :: IOish m => GhcModState -> GhcModT m ()-gmsPut = GhcModT . put--options :: IOish m => GhcModT m Options-options = gmOptions <$> gmeAsk--cradle :: IOish m => GhcModT m Cradle-cradle = gmCradle <$> gmeAsk--getCompilerMode :: IOish m => GhcModT m CompilerMode-getCompilerMode = gmCompilerMode <$> gmsGet--setCompilerMode :: IOish m => CompilerMode -> GhcModT m ()-setCompilerMode mode = (\s -> gmsPut s { gmCompilerMode = mode } ) =<< gmsGet--------------------------------------------------------------------withOptions :: IOish m => (Options -> Options) -> GhcModT m a -> GhcModT m a-withOptions changeOpt action = local changeEnv action- where- changeEnv e = e { gmOptions = changeOpt opt }- where- opt = gmOptions e--------------------------------------------------------------------instance (MonadBaseControl IO m) => MonadBase IO (GhcModT m) where- liftBase = GhcModT . liftBase--instance (MonadBaseControl IO m) => MonadBaseControl IO (GhcModT m) where- newtype StM (GhcModT m) a = StGhcMod {- unStGhcMod :: StM (StateT GhcModState- (ErrorT GhcModError- (JournalT GhcModLog- (ReaderT GhcModEnv m) ) ) ) a }- liftBaseWith f = GhcModT . liftBaseWith $ \runInBase ->- f $ liftM StGhcMod . runInBase . unGhcModT-- restoreM = GhcModT . restoreM . unStGhcMod- {-# INLINE liftBaseWith #-}- {-# INLINE restoreM #-}---- GHC cannot prove the following instances to be decidable automatically using--- the FlexibleContexts extension as they violate the second Paterson Condition,--- namely that: The assertion has fewer constructors and variables (taken--- together and counting repetitions) than the head. Specifically the--- @MonadBaseControl IO m@ constraint is causing this violation.------ Proof of termination:------ Assuming all constraints containing the variable `m' exist and are decidable--- we show termination by manually replacing the current set of constraints with--- their own set of constraints and show that this, after a finite number of--- steps, results in the empty set, i.e. not having to check any more--- constraints.------ We start by setting the constraints to be those immediate constraints of the--- instance declaration which cannot be proven decidable automatically for the--- type under consideration.------ @--- { MonadBaseControl IO m }--- @------ Classes used:------ * @class MonadBase b m => MonadBaseControl b m@------ @--- { MonadBase IO m }--- @------ Classes used:------ * @class (Applicative b, Applicative m, Monad b, Monad m) => MonadBase b m@------ @--- { Applicative IO, Applicative m, Monad IO, Monad m }--- @------ Classes used:------ * @class Monad m@--- * @class Applicative f => Functor f@------ @--- { Functor m }--- @------ Classes used:------ * @class Functor f@------ @--- { }--- @--- ∎--instance (Functor m, MonadIO m, MonadBaseControl IO m)- => GhcMonad (GhcModT m) where- getSession = (liftIO . readIORef) . gmGhcSession =<< ask- setSession a = (liftIO . flip writeIORef a) . gmGhcSession =<< ask--#if __GLASGOW_HASKELL__ >= 706-instance (Functor m, MonadIO m, MonadBaseControl IO m)- => HasDynFlags (GhcModT m) where- getDynFlags = getSessionDynFlags-#endif--instance (MonadIO m, MonadBaseControl IO m)- => ExceptionMonad (GhcModT m) where- gcatch act handler = control $ \run ->- run act `gcatch` (run . handler)-- gmask = liftBaseOp gmask . liftRestore- where liftRestore f r = f $ liftBaseOp_ r
− Language/Haskell/GhcMod/PkgDoc.hs
@@ -1,26 +0,0 @@-module Language.Haskell.GhcMod.PkgDoc (pkgDoc) where--import Language.Haskell.GhcMod.Types-import Language.Haskell.GhcMod.GhcPkg-import Language.Haskell.GhcMod.Monad-import Language.Haskell.GhcMod.Utils--import Control.Applicative ((<$>))---- | Obtaining the package name and the doc path of a module.-pkgDoc :: IOish m => String -> GhcModT m String-pkgDoc mdl = do- c <- cradle- pkg <- trim <$> readProcess' "ghc-pkg" (toModuleOpts c)- if pkg == "" then- return "\n"- else do- htmlpath <- readProcess' "ghc-pkg" (toDocDirOpts pkg c)- let ret = pkg ++ " " ++ drop 14 htmlpath- return ret- where- toModuleOpts c = ["find-module", mdl, "--simple-output"]- ++ ghcPkgDbStackOpts (cradlePkgDbStack c)- toDocDirOpts pkg c = ["field", pkg, "haddock-html"]- ++ ghcPkgDbStackOpts (cradlePkgDbStack c)- trim = takeWhile (`notElem` " \n")
− Language/Haskell/GhcMod/Read.hs
@@ -1,106 +0,0 @@-module Language.Haskell.GhcMod.Read where--import Text.Read (readPrec_to_S, readPrec, minPrec)-import qualified Text.ParserCombinators.ReadP as P-import Text.ParserCombinators.ReadPrec (lift)---- This library (libraries/base) is derived from code from several--- sources:---- * Code from the GHC project which is largely (c) The University of--- Glasgow, and distributable under a BSD-style license (see below),---- * Code from the Haskell 98 Report which is (c) Simon Peyton Jones--- and freely redistributable (but see the full license for--- restrictions).---- * Code from the Haskell Foreign Function Interface specification,--- which is (c) Manuel M. T. Chakravarty and freely redistributable--- (but see the full license for restrictions).---- The full text of these licenses is reproduced below. All of the--- licenses are BSD-style or compatible.---- --------------------------------------------------------------------------------- The Glasgow Haskell Compiler License---- Copyright 2004, The University Court of the University of Glasgow.--- All rights reserved.---- Redistribution and use in source and binary forms, with or without--- modification, are permitted provided that the following conditions are met:---- - Redistributions of source code must retain the above copyright notice,--- this list of conditions and the following disclaimer.---- - Redistributions in binary form must reproduce the above copyright notice,--- this list of conditions and the following disclaimer in the documentation--- and/or other materials provided with the distribution.---- - Neither name of the University nor the names of its contributors may be--- used to endorse or promote products derived from this software without--- specific prior written permission.---- THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY COURT OF THE UNIVERSITY OF--- GLASGOW AND THE CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,--- INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND--- FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE--- UNIVERSITY COURT OF THE UNIVERSITY OF GLASGOW OR THE CONTRIBUTORS BE LIABLE--- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL--- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR--- SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER--- CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT--- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY--- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH--- DAMAGE.---- --------------------------------------------------------------------------------- Code derived from the document "Report on the Programming Language--- Haskell 98", is distributed under the following license:---- Copyright (c) 2002 Simon Peyton Jones---- The authors intend this Report to belong to the entire Haskell--- community, and so we grant permission to copy and distribute it for--- any purpose, provided that it is reproduced in its entirety,--- including this Notice. Modified versions of this Report may also be--- copied and distributed for any purpose, provided that the modified--- version is clearly presented as such, and that it does not claim to--- be a definition of the Haskell 98 Language.---- --------------------------------------------------------------------------------- Code derived from the document "The Haskell 98 Foreign Function--- Interface, An Addendum to the Haskell 98 Report" is distributed under--- the following license:---- Copyright (c) 2002 Manuel M. T. Chakravarty---- The authors intend this Report to belong to the entire Haskell--- community, and so we grant permission to copy and distribute it for--- any purpose, provided that it is reproduced in its entirety,--- including this Notice. Modified versions of this Report may also be--- copied and distributed for any purpose, provided that the modified--- version is clearly presented as such, and that it does not claim to--- be a definition of the Haskell 98 Foreign Function Interface.---- -------------------------------------------------------------------------------readEither :: Read a => String -> Either String a-readEither s =- case [ x | (x,"") <- readPrec_to_S read' minPrec s ] of- [x] -> Right x- [] -> Left "Prelude.read: no parse"- _ -> Left "Prelude.read: ambiguous parse"- where- read' =- do x <- readPrec- lift P.skipSpaces- return x--readMaybe :: Read a => String -> Maybe a-readMaybe s = case readEither s of- Left _ -> Nothing- Right a -> Just a
− Language/Haskell/GhcMod/SrcUtils.hs
@@ -1,106 +0,0 @@-{-# LANGUAGE TupleSections, FlexibleInstances, Rank2Types #-}-{-# OPTIONS_GHC -fno-warn-orphans #-}--module Language.Haskell.GhcMod.SrcUtils where--import Control.Applicative ((<$>))-import CoreUtils (exprType)-import Data.Generics-import Data.Maybe (fromMaybe)-import Data.Ord as O-import GHC (LHsExpr, LPat, Id, DynFlags, SrcSpan, Type, Located, ParsedSource, RenamedSource, TypecheckedSource, GenLocated(L))-import qualified GHC as G-import GHC.SYB.Utils (Stage(..), everythingStaged)-import GhcMonad-import qualified Language.Haskell.Exts.Annotated as HE-import Language.Haskell.GhcMod.Doc (showOneLine, getStyle)-import Language.Haskell.GhcMod.DynFlags-import Language.Haskell.GhcMod.Gap (HasType(..), setWarnTypedHoles, setDeferTypeErrors)-import qualified Language.Haskell.GhcMod.Gap as Gap-import Language.Haskell.GhcMod.Monad (IOish, GhcModT)-import Language.Haskell.GhcMod.Target (setTargetFiles)-import OccName (OccName)-import Outputable (PprStyle)-import TcHsSyn (hsPatType)--------------------------------------------------------------------instance HasType (LHsExpr Id) where- getType tcm e = do- hs_env <- G.getSession- mbe <- liftIO $ Gap.deSugar tcm e hs_env- return $ (G.getLoc e, ) <$> CoreUtils.exprType <$> mbe--instance HasType (LPat Id) where- getType _ (G.L spn pat) = return $ Just (spn, hsPatType pat)--------------------------------------------------------------------listifySpans :: Typeable a => TypecheckedSource -> (Int, Int) -> [Located a]-listifySpans tcs lc = listifyStaged TypeChecker p tcs- where- p (L spn _) = G.isGoodSrcSpan spn && spn `G.spans` lc--listifyParsedSpans :: Typeable a => ParsedSource -> (Int, Int) -> [Located a]-listifyParsedSpans pcs lc = listifyStaged Parser p pcs- where- p (L spn _) = G.isGoodSrcSpan spn && spn `G.spans` lc--listifyRenamedSpans :: Typeable a => RenamedSource -> (Int, Int) -> [Located a]-listifyRenamedSpans pcs lc = listifyStaged Renamer p pcs- where- p (L spn _) = G.isGoodSrcSpan spn && spn `G.spans` lc--listifyStaged :: Typeable r => Stage -> (r -> Bool) -> GenericQ [r]-listifyStaged s p = everythingStaged s (++) [] ([] `mkQ` (\x -> [x | p x]))--cmp :: SrcSpan -> SrcSpan -> Ordering-cmp a b- | a `G.isSubspanOf` b = O.LT- | b `G.isSubspanOf` a = O.GT- | otherwise = O.EQ--toTup :: DynFlags -> PprStyle -> (SrcSpan, Type) -> ((Int,Int,Int,Int),String)-toTup dflag style (spn, typ) = (fourInts spn, pretty dflag style typ)--fourInts :: SrcSpan -> (Int,Int,Int,Int)-fourInts = fromMaybe (0,0,0,0) . Gap.getSrcSpan--fourIntsHE :: HE.SrcSpan -> (Int,Int,Int,Int)-fourIntsHE loc = ( HE.srcSpanStartLine loc, HE.srcSpanStartColumn loc- , HE.srcSpanEndLine loc, HE.srcSpanEndColumn loc)---- Check whether (line,col) is inside a given SrcSpanInfo-typeSigInRangeHE :: Int -> Int -> HE.Decl HE.SrcSpanInfo -> Bool-typeSigInRangeHE lineNo colNo (HE.TypeSig (HE.SrcSpanInfo s _) _ _) =- HE.srcSpanStart s <= (lineNo, colNo) && HE.srcSpanEnd s >= (lineNo, colNo)-typeSigInRangeHE lineNo colNo (HE.TypeFamDecl (HE.SrcSpanInfo s _) _ _) =- HE.srcSpanStart s <= (lineNo, colNo) && HE.srcSpanEnd s >= (lineNo, colNo)-typeSigInRangeHE lineNo colNo (HE.DataFamDecl (HE.SrcSpanInfo s _) _ _ _) =- HE.srcSpanStart s <= (lineNo, colNo) && HE.srcSpanEnd s >= (lineNo, colNo)-typeSigInRangeHE _ _ _= False--pretty :: DynFlags -> PprStyle -> Type -> String-pretty dflag style = showOneLine dflag style . Gap.typeForUser--------------------------------------------------------------------inModuleContext :: IOish m- => FilePath- -> (DynFlags -> PprStyle -> GhcModT m a)- -> GhcModT m a-inModuleContext file action =- withDynFlags (setWarnTypedHoles . setDeferTypeErrors . setNoWarningFlags) $ do- setTargetFiles [file]- Gap.withContext $ do- dflag <- G.getSessionDynFlags- style <- getStyle- action dflag style--------------------------------------------------------------------showName :: DynFlags -> PprStyle -> G.Name -> String-showName dflag style name = showOneLine dflag style $ Gap.nameForUser name--showOccName :: DynFlags -> PprStyle -> OccName -> String-showOccName dflag style name = showOneLine dflag style $ Gap.occNameForUser name
− Language/Haskell/GhcMod/Target.hs
@@ -1,60 +0,0 @@-{-# LANGUAGE CPP #-}-module Language.Haskell.GhcMod.Target (- setTargetFiles- ) where--import Control.Applicative ((<$>))-import Control.Monad (forM, void, (>=>))-import DynFlags (ExtensionFlag(..), xopt)-import GHC (LoadHowMuch(..))-import qualified GHC as G-import Language.Haskell.GhcMod.DynFlags-import Language.Haskell.GhcMod.Monad---- | Set the files as targets and load them.-setTargetFiles :: IOish m => [FilePath] -> GhcModT m ()-setTargetFiles files = do- targets <- forM files $ \file -> G.guessTarget file Nothing- G.setTargets targets- mode <- getCompilerMode- if mode == Intelligent then- loadTargets Intelligent- else do- mdls <- G.depanal [] False- let fallback = needsFallback mdls- if fallback then do- resetTargets targets- setIntelligent- loadTargets Intelligent- else- loadTargets Simple- where- loadTargets Simple = do- -- Reporting error A and error B- void $ G.load LoadAllTargets- mss <- filter (\x -> G.ms_hspp_file x `elem` files) <$> G.getModuleGraph- -- Reporting error B and error C- mapM_ (G.parseModule >=> G.typecheckModule >=> G.desugarModule) mss- -- Error B duplicates. But we cannot ignore both error reportings,- -- sigh. So, the logger makes log messages unique by itself.- loadTargets Intelligent = do- df <- G.getSessionDynFlags- void $ G.setSessionDynFlags (setModeIntelligent df)- void $ G.load LoadAllTargets- resetTargets targets = do- G.setTargets []- void $ G.load LoadAllTargets- G.setTargets targets- setIntelligent = do- newdf <- setModeIntelligent <$> G.getSessionDynFlags- void $ G.setSessionDynFlags newdf- setCompilerMode Intelligent--needsFallback :: G.ModuleGraph -> Bool-needsFallback = any $ \ms ->- let df = G.ms_hspp_opts ms in- Opt_TemplateHaskell `xopt` df- || Opt_QuasiQuotes `xopt` df-#if __GLASGOW_HASKELL__ >= 708- || (Opt_PatternSynonyms `xopt` df)-#endif
− Language/Haskell/GhcMod/Types.hs
@@ -1,122 +0,0 @@-module Language.Haskell.GhcMod.Types where--import Control.Monad.Trans.Control (MonadBaseControl)-import Data.List (intercalate)-import qualified Data.Map as M-import Exception (ExceptionMonad)-import MonadUtils (MonadIO)--import PackageConfig (PackageConfig)---- | A constraint alias (-XConstraintKinds) to make functions dealing with--- 'GhcModT' somewhat cleaner.------ Basicially an @IOish m => m@ is a 'Monad' supporting arbitrary 'IO' and--- exception handling. Usually this will simply be 'IO' but we parametrise it in--- the exported API so users have the option to use a custom inner monad.-type IOish m = (Functor m, MonadIO m, MonadBaseControl IO m, ExceptionMonad m)---- | Output style.-data OutputStyle = LispStyle -- ^ S expression style.- | PlainStyle -- ^ Plain textstyle.---- | The type for line separator. Historically, a Null string is used.-newtype LineSeparator = LineSeparator String--data Options = Options {- outputStyle :: OutputStyle- , hlintOpts :: [String]- -- | GHC command line options set on the @ghc-mod@ command line- , ghcUserOptions:: [GHCOption]- -- | If 'True', 'browse' also returns operators.- , operators :: Bool- -- | If 'True', 'browse' also returns types.- , detailed :: Bool- -- | If 'True', 'browse' will return fully qualified name- , qualified :: Bool- -- | Line separator string.- , lineSeparator :: LineSeparator- }---- | A default 'Options'.-defaultOptions :: Options-defaultOptions = Options {- outputStyle = PlainStyle- , hlintOpts = []- , ghcUserOptions= []- , operators = False- , detailed = False- , qualified = False- , lineSeparator = LineSeparator "\0"- }---------------------------------------------------------------------- | The environment where this library is used.-data Cradle = Cradle {- -- | The directory where this library is executed.- cradleCurrentDir :: FilePath- -- | The project root directory.- , cradleRootDir :: FilePath- -- | The file name of the found cabal file.- , cradleCabalFile :: Maybe FilePath- -- | Package database stack- , cradlePkgDbStack :: [GhcPkgDb]- } deriving (Eq, Show)---------------------------------------------------------------------- | GHC package database flags.-data GhcPkgDb = GlobalDb | UserDb | PackageDb String deriving (Eq, Show)---- | A single GHC command line option.-type GHCOption = String---- | An include directory for modules.-type IncludeDir = FilePath---- | A package name.-type PackageBaseName = String---- | A package version.-type PackageVersion = String---- | A package id.-type PackageId = String---- | A package's name, verson and id.-type Package = (PackageBaseName, PackageVersion, PackageId)--pkgName :: Package -> PackageBaseName-pkgName (n,_,_) = n--pkgVer :: Package -> PackageVersion-pkgVer (_,v,_) = v--pkgId :: Package -> PackageId-pkgId (_,_,i) = i--showPkg :: Package -> String-showPkg (n,v,_) = intercalate "-" [n,v]--showPkgId :: Package -> String-showPkgId (n,v,i) = intercalate "-" [n,v,i]---- | Collection of packages-type PkgDb = (M.Map Package PackageConfig)---- | Haskell expression.-type Expression = String---- | Module name.-type ModuleString = String---- | A Module-type Module = [String]---- | Option information for GHC-data CompilerOptions = CompilerOptions {- ghcOptions :: [GHCOption] -- ^ Command line options- , includeDirs :: [IncludeDir] -- ^ Include directories for modules- , depPackages :: [Package] -- ^ Dependent package names- } deriving (Eq, Show)
− Language/Haskell/GhcMod/Utils.hs
@@ -1,44 +0,0 @@-module Language.Haskell.GhcMod.Utils where---import Language.Haskell.GhcMod.Error-import MonadUtils (MonadIO, liftIO)-import System.Directory (getCurrentDirectory, setCurrentDirectory)-import System.Exit (ExitCode(..))-import System.Process (readProcessWithExitCode)---- dropWhileEnd is not provided prior to base 4.5.0.0.-dropWhileEnd :: (a -> Bool) -> [a] -> [a]-dropWhileEnd p = foldr (\x xs -> if p x && null xs then [] else x : xs) []--extractParens :: String -> String-extractParens str = extractParens' str 0- where- extractParens' :: String -> Int -> String- extractParens' [] _ = []- extractParens' (s:ss) level- | s `elem` "([{" = s : extractParens' ss (level+1)- | level == 0 = extractParens' ss 0- | s `elem` "}])" && level == 1 = [s]- | s `elem` "}])" = s : extractParens' ss (level-1)- | otherwise = s : extractParens' ss level--readProcess' :: (MonadIO m, MonadError GhcModError m)- => String- -> [String]- -> m String-readProcess' cmd opts = do- (rv,output,err) <- liftIO (readProcessWithExitCode cmd opts "")- `modifyError'` GMEProcess ([cmd] ++ opts)- case rv of- ExitFailure val -> do- throwError $ GMEProcess ([cmd] ++ opts) $ strMsg $- cmd ++ " " ++ unwords opts ++ " (exit " ++ show val ++ ")"- ++ "\n" ++ err- ExitSuccess ->- return output--withDirectory_ :: (MonadIO m, ExceptionMonad m) => FilePath -> m a -> m a-withDirectory_ dir action =- gbracket (liftIO getCurrentDirectory) (liftIO . setCurrentDirectory)- (\_ -> liftIO (setCurrentDirectory dir) >> action)
+ README.md view
@@ -0,0 +1,99 @@+# ghc-mod: Happy Haskell Hacking+[](https://gitlab.com/dxld/ghc-mod/commits/master)++ghc-mod provides editors/IDEs with support for Haskell compiler features, it+supports both Cabal and Stack based projects and integrations exist for Emacs,+Vim, Atom, IntelliJ and VSCode.++- for [all Haskell developers (Using ghc-mod in your development environment)](#using-ghc-mod-in-your-development-environment)+- for [people developing Haskell IDEs (Using ghc-mod as an IDE backend program)](#using-ghc-mod-as-an-ide-backend-program)+- for [developing Haskell tooling (Using ghc-mod as a library)](#using-ghc-mod-as-a-library)++## Overview++### Using ghc-mod in your Development Environment++To use `ghc-mod` in your development environment of choice you need two things:++ - The `ghc-mod` program included in the package of the same name, see [Installing](https://github.com/DanielG/ghc-mod/wiki/Installing)+ - A ghc-mod frontend to integrate it into your development environment, see [Frontend](https://github.com/DanielG/ghc-mod/wiki/Frontend)++### Using ghc-mod as an IDE Backend Program++We provide two modes of operation for frontends: interactive and single shot+mode. The former is accessed by calling `$ ghc-mod legacy-interactive` this will+sit and wait for you to type a command and exit when an empty line is+entered. Interactive mode is pretty much always faster than single shot mode+since it gives ghc-mod the ability to cache the compiler session between+commands on the other hand it needs more memory because it keeps these things+cached. ++Single shot mode is pretty much only there for (backwards) compatibility with+Vim since it only recently got the ability to talk to background processes+without installing some external plugin. You can use single-shot mode by simply+calling the sub-comamnds of the `ghc-mod` program. Since re-compiling large+projects can be really, really slow you really shouldn't use this and use+interactive mode instead.++As a rule of thumb all commands available in single shot mode are available in+interactive mode, a list of the former can be obtained by running +`$ ghc-mod --help`.++If you're developing a new ghc-mod fronted we'd love to hear from you! Please+open an issue or e-mail the maintainer. Also we invite you to add installation+and configuration instructions to+[Frontend](https://github.com/DanielG/ghc-mod/wiki/Frontend).++### Using ghc-mod as a Library++Internally ghc-mod uses the Glasgow Haskell Compilers's API to implement most of+it's functionality.++In order to provide a hassle free experience to users ghc-mod tries hard to+automatically, and correctly, detect and if needed tweak the environment GHC+needs. It also handles some of the more cumbersome parts of getting a working+compiler session up and running.++This functionality can be very useful to all kinds of Haskell development tools+therefore want to expose all the useful abstractions ghc-mod provides.++Right now the ghc-mod API is pretty messy; a result of major internal rewrites+and reorganization coupled with too little time for cleanups over the course of+almost 100 releases! We would like to make a cut during v6.0 or so and+completely re-do the API but we need more input from downstream tool writers to+do that properly, see [Library API Redesign](Library-API-Redesign.md).++For example [The Haskell Refactorer (HaRe)](https://github.com/alanz/HaRe) uses+the build environment abstraction ghc-mod provides so it can concentrate on it's+core functionality instead of worrying about build environments and compiler+session setup.++Recently the+[`haskell-ide-engine`](https://github.com/haskell/haskell-ide-engine) project+has sprung up and if you're planning to write any kind of tool that needs editor+integration eventually you should definetly look into that. `haskell-ide-engine`+uses `ghc-mod` at it's core so you'll want to be familliar with it either way.++API "documentation" is here:+[Hackage docs](https://hackage.haskell.org/package/ghc-mod#modules).++## Reporting Bugs++Please report bugs on the GitHub issue tracker for ghc-mod:+https://github.com/DanielG/ghc-mod/issues++Including general environment information like the operating system+(distribution, version) you're using and the output of `$ ghc-mod debug` run in+your project directory is probably a good idea.++## IRC++If you have any problems, suggestions, comments swing by+[\#ghc-mod (web client)](https://kiwiirc.com/client/irc.freenode.org/ghc-mod) on+Freenode. If you're reporting a bug please also create an issue+[here (GitHub issue tracker)](https://github.com/DanielG/ghc-mod/issues) so we+have a way to contact you if you don't have time to stay.++Do hang around for a while if no one answers and repeat your question if you+still haven't gotten any answer after a day or so (the maintainer was probably+asleep). You're most likely to get an answer during the day in GMT+1.
Setup.hs view
@@ -1,2 +1,107 @@+#!/usr/bin/env runhaskell+{-# LANGUAGE RecordWildCards, NamedFieldPuns #-} import Distribution.Simple-main = defaultMain+import Distribution.Simple.Utils+import Distribution.Simple.Setup+import Distribution.Simple.Install+import Distribution.Simple.Program+import Distribution.Simple.Register+import Distribution.Simple.BuildPaths+import qualified Distribution.Simple.InstallDirs as ID+import Distribution.Simple.LocalBuildInfo+import Distribution.PackageDescription++import qualified Data.Map as M+import Data.Map (Map)++import Control.Arrow+import Control.Applicative+import Control.Monad+import Data.List+import Data.Maybe+import Data.Version+import Data.Monoid+import System.Process+import System.Exit+import System.FilePath+import System.Directory (renameFile)++main :: IO ()+main = defaultMainWithHooks $ simpleUserHooks {+ instHook = inst,+ copyHook = copy,+ buildHook = \pd lbi hooks flags -> (buildHook simpleUserHooks) pd (patchLibexecdir lbi) hooks flags,+ hookedPrograms = [ simpleProgram "shelltest" ]+ }++patchLibexecdir :: LocalBuildInfo -> LocalBuildInfo+patchLibexecdir lbi = let+ idirtpl = installDirTemplates lbi+ libexecdir' = toPathTemplate $ fromPathTemplate (libexecdir idirtpl) </> "$abi/$pkgid"+ lbi' = lbi { installDirTemplates = idirtpl { libexecdir = libexecdir' } }+ in+ lbi'++-- mostly copypasta from 'defaultInstallHook'+inst ::+ PackageDescription -> LocalBuildInfo -> UserHooks -> InstallFlags -> IO ()+inst pd lbi _uf ifl = do+ let copyFlags = defaultCopyFlags {+ copyDistPref = installDistPref ifl,+ copyDest = toFlag NoCopyDest,+ copyVerbosity = installVerbosity ifl+ }+ xInstallTarget pd lbi copyFlags (\pd' lbi' -> install pd' lbi' copyFlags)+ let registerFlags = defaultRegisterFlags {+ regDistPref = installDistPref ifl,+ regInPlace = installInPlace ifl,+ regPackageDB = installPackageDB ifl,+ regVerbosity = installVerbosity ifl+ }+ when (hasLibs pd) $ register pd lbi registerFlags++copy :: PackageDescription -> LocalBuildInfo -> UserHooks -> CopyFlags -> IO ()+copy pd lbi _uh cf =+ xInstallTarget pd lbi cf (\pd' lbi' -> install pd' lbi' cf)++xInstallTarget :: PackageDescription+ -> LocalBuildInfo+ -> CopyFlags+ -> (PackageDescription -> LocalBuildInfo -> IO ())+ -> IO ()+xInstallTarget pd lbi cf fn = do+ let (extended, regular) = partition isInternal (executables pd)++ let pd_regular = pd { executables = regular }++ _ <- flip mapM extended $ \exe -> do+ let pd_extended = onlyExePackageDesc [exe] pd+ fn pd_extended lbi++ let lbi' = patchLibexecdir lbi+ copydest = fromFlag (copyDest cf)+ verbosity = fromFlag (copyVerbosity cf)+ InstallDirs { bindir, libexecdir } = absoluteInstallDirs pd lbi' copydest+ progprefix = substPathTemplate (packageId pd) lbi (progPrefix lbi)+ progsuffix = substPathTemplate (packageId pd) lbi (progSuffix lbi)+ fixedExeBaseName = progprefix ++ exeName exe ++ progsuffix++ fixedExeFileName = bindir </> fixedExeBaseName <.> exeExtension+ newExeFileName = libexecdir </> fixedExeBaseName <.> exeExtension++ when (exeName exe == "ghc-mod-real") $ do+ createDirectoryIfMissingVerbose verbosity True libexecdir+ renameFile fixedExeFileName newExeFileName++ fn pd_regular lbi++ where+ isInternal :: Executable -> Bool+ isInternal exe =+ fromMaybe False $ (=="True") <$> lookup "x-internal" (customFieldsBI $ buildInfo exe)++onlyExePackageDesc :: [Executable] -> PackageDescription -> PackageDescription+onlyExePackageDesc exes pd = emptyPackageDescription {+ package = package pd+ , executables = exes+ }
+ bench/Bench.hs view
@@ -0,0 +1,30 @@+import Criterion.Main+import GhcMod.Target+import GhcMod.Monad+import GhcMod.Types+import Dir+import System.IO.Temp+import System.Process hiding (env)+import Control.Monad++main = defaultMain [+ env setup $ \dir -> bgroup "simple-cabal" [+ bench "nop" $ whnfIO (simpleCabalNop dir 1)+ , bench "nop10" $ whnfIO (simpleCabalNop dir 10)+ ]+ ]++setup = do+ tdir <- createTempDirectory "/tmp" "ghc-mod-bench"+ system $ "cp -rv \"bench/data/simple-cabal/\" \""++ tdir ++"\""++ simpleCabalNop tdir 1 -- warmup dist/++ return tdir++simpleCabalNop :: FilePath -> Int -> IO ()+simpleCabalNop dir n = withDirectory_ (dir </> "simple-cabal") $ do+ _ <- runGhcModT defaultOptions $+ forM_ [1..n] $ \_ -> do+ runGmlT [Left "Main.hs"] (return ())+ return ()
+ bench/data/simple-cabal/Main.hs view
@@ -0,0 +1,4 @@+module Main where++main :: IO ()+main = putStrLn "Hello, Haskell!"
+ bench/data/simple-cabal/Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ bench/data/simple-cabal/simple-cabal.cabal view
@@ -0,0 +1,13 @@+name: simple-cabal+version: 0.1.0.0+license: BSD3+license-file: LICENSE+author: Daniel Gröber+maintainer: dxld@darkboxed.org+build-type: Simple+cabal-version: >=1.10++executable simple-cabal+ main-is: Main.hs+ build-depends: base+ default-language: Haskell2010
+ core/Data/Binary/Generic.hs view
@@ -0,0 +1,133 @@+{-# LANGUAGE BangPatterns, CPP, FlexibleInstances, KindSignatures,+ ScopedTypeVariables, TypeOperators, TypeSynonymInstances #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}++-----------------------------------------------------------------------------+-- |+-- Module : Data.Binary.Generic+-- Copyright : Bryan O'Sullivan+-- License : BSD3-style (see LICENSE)+--+-- Maintainer : Bryan O'Sullivan <bos@serpentine.com>+-- Stability : unstable+-- Portability : Only works with GHC 7.2 and newer+--+-- Instances for supporting GHC generics.+--+-----------------------------------------------------------------------------+module Data.Binary.Generic where++import Control.Applicative+import Data.Binary+import Data.Bits+import GHC.Generics+import Prelude++class GGBinary f where+ ggput :: f t -> Put+ ggget :: Get (f t)++-- Type without constructors+instance GGBinary V1 where+ ggput _ = return ()+ ggget = return undefined++-- Constructor without arguments+instance GGBinary U1 where+ ggput U1 = return ()+ ggget = return U1++-- Product: constructor with parameters+instance (GGBinary a, GGBinary b) => GGBinary (a :*: b) where+ ggput (x :*: y) = ggput x >> ggput y+ ggget = (:*:) <$> ggget <*> ggget++-- Metadata (constructor name, etc)+instance GGBinary a => GGBinary (M1 i c a) where+ ggput = ggput . unM1+ ggget = M1 <$> ggget++-- Constants, additional parameters, and rank-1 recursion+instance Binary a => GGBinary (K1 i a) where+ ggput = put . unK1+ ggget = K1 <$> get++-- Borrowed from the cereal package.++-- The following GGBinary instance for sums has support for serializing+-- types with up to 2^64-1 constructors. It will use the minimal+-- number of bytes needed to encode the constructor. For example when+-- a type has 2^8 constructors or less it will use a single byte to+-- encode the constructor. If it has 2^16 constructors or less it will+-- use two bytes, and so on till 2^64-1.++#define GUARD(WORD) (size - 1) <= fromIntegral (maxBound :: WORD)+#define PUTSUM(WORD) GUARD(WORD) = putSum (0 :: WORD) (fromIntegral size)+#define GETSUM(WORD) GUARD(WORD) = (get :: Get WORD) >>= checkGetSum (fromIntegral size)++instance ( GSum a, GSum b+ , SumSize a, SumSize b) => GGBinary (a :+: b) where+ ggput | PUTSUM(Word8) | PUTSUM(Word16) | PUTSUM(Word32) | PUTSUM(Word64)+ | otherwise = sizeError "encode" size+ where+ size = unTagged (sumSize :: Tagged (a :+: b) Word64)+ {-# INLINE ggput #-}++ ggget | GETSUM(Word8) | GETSUM(Word16) | GETSUM(Word32) | GETSUM(Word64)+ | otherwise = sizeError "decode" size+ where+ size = unTagged (sumSize :: Tagged (a :+: b) Word64)+ {-# INLINE ggget #-}++sizeError :: Show size => String -> size -> error+sizeError s size =+ error $ "Can't " ++ s ++ " a type with " ++ show size ++ " constructors"++------------------------------------------------------------------------++checkGetSum :: (Ord word, Num word, Bits word, GSum f)+ => word -> word -> Get (f a)+checkGetSum size code | code < size = getSum code size+ | otherwise = fail "Unknown encoding for constructor"+{-# INLINE checkGetSum #-}++class GSum f where+ getSum :: (Ord word, Num word, Bits word) => word -> word -> Get (f a)+ putSum :: (Num w, Bits w, Binary w) => w -> w -> f a -> Put++instance (GSum a, GSum b) => GSum (a :+: b) where+ getSum !code !size | code < sizeL = L1 <$> getSum code sizeL+ | otherwise = R1 <$> getSum (code - sizeL) sizeR+ where+ sizeL = size `shiftR` 1+ sizeR = size - sizeL+ {-# INLINE getSum #-}++ putSum !code !size s = case s of+ L1 x -> putSum code sizeL x+ R1 x -> putSum (code + sizeL) sizeR x+ where+ sizeL = size `shiftR` 1+ sizeR = size - sizeL+ {-# INLINE putSum #-}++instance GGBinary a => GSum (C1 c a) where+ getSum _ _ = ggget+ {-# INLINE getSum #-}++ putSum !code _ x = put code *> ggput x+ {-# INLINE putSum #-}++------------------------------------------------------------------------++class SumSize f where+ sumSize :: Tagged f Word64++newtype Tagged (s :: * -> *) b = Tagged {unTagged :: b}++instance (SumSize a, SumSize b) => SumSize (a :+: b) where+ sumSize = Tagged $ unTagged (sumSize :: Tagged a Word64) ++ unTagged (sumSize :: Tagged b Word64)++instance SumSize (C1 c a) where+ sumSize = Tagged 1
+ core/GhcMod/CabalHelper.hs view
@@ -0,0 +1,309 @@+-- ghc-mod: Happy Haskell Hacking+-- Copyright (C) 2015 Daniel Gröber <dxld ÄT darkboxed DOT org>+--+-- This program is free software: you can redistribute it and/or modify+-- it under the terms of the GNU Affero General Public License as published by+-- the Free Software Foundation, either version 3 of the License, or+-- (at your option) any later version.+--+-- This program is distributed in the hope that it will be useful,+-- but WITHOUT ANY WARRANTY; without even the implied warranty of+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+-- GNU Affero General Public License for more details.+--+-- You should have received a copy of the GNU Affero General Public License+-- along with this program. If not, see <http://www.gnu.org/licenses/>.++{-# LANGUAGE CPP #-}+module GhcMod.CabalHelper+ ( getComponents+ , getGhcMergedPkgOptions+ , getCabalPackageDbStack+ , prepareCabalHelper+ , withAutogen+ , withCabal+ ) where++import Control.Applicative+import Control.Monad+import Control.Category ((.))+import Data.Maybe+import Data.Monoid+import Data.Version+import Data.Binary (Binary)+import Data.Traversable+import Distribution.Helper hiding (Programs(..))+import qualified Distribution.Helper as CH+import qualified GhcMod.Types as T+import GhcMod.Types+import GhcMod.Monad.Types+import GhcMod.Utils+import GhcMod.PathsAndFiles+import GhcMod.Logging+import GhcMod.Output+import GhcMod.CustomPackageDb+import GhcMod.Stack+import System.FilePath+import System.Process+import System.Exit+import Prelude hiding ((.))++import Paths_ghc_mod as GhcMod++-- | Only package related GHC options, sufficient for things that don't need to+-- access home modules+getGhcMergedPkgOptions :: (Applicative m, IOish m, Gm m)+ => m [GHCOption]+getGhcMergedPkgOptions = chCached $ \distdir -> Cached {+ cacheLens = Just (lGmcMergedPkgOptions . lGmCaches),+ cacheFile = mergedPkgOptsCacheFile distdir,+ cachedAction = \_tcf (_progs, _projdir, _ver) _ma -> do+ opts <- runCHQuery ghcMergedPkgOptions+ return ([setupConfigPath distdir], opts)+ }++getCabalPackageDbStack :: (IOish m, Gm m) => m [GhcPkgDb]+getCabalPackageDbStack = chCached $ \distdir -> Cached {+ cacheLens = Just (lGmcPackageDbStack . lGmCaches),+ cacheFile = pkgDbStackCacheFile distdir,+ cachedAction = \_tcf (_progs, _projdir, _ver) _ma -> do+ crdl <- cradle+ dbs <- map chPkgToGhcPkg <$>+ runCHQuery packageDbStack+ return ([setupConfigFile crdl, sandboxConfigFile crdl], dbs)+ }++chPkgToGhcPkg :: ChPkgDb -> GhcPkgDb+chPkgToGhcPkg ChPkgGlobal = GlobalDb+chPkgToGhcPkg ChPkgUser = UserDb+chPkgToGhcPkg (ChPkgSpecific f) = PackageDb f++-- | Primary interface to cabal-helper and intended single entrypoint to+-- constructing 'GmComponent's+--+-- The Component\'s 'gmcHomeModuleGraph' will be empty and has to be resolved by+-- 'resolveGmComponents'.+getComponents :: (Applicative m, IOish m, Gm m)+ => m [GmComponent 'GMCRaw ChEntrypoint]+getComponents = chCached $ \distdir -> Cached {+ cacheLens = Just (lGmcComponents . lGmCaches),+ cacheFile = cabalHelperCacheFile distdir,+ cachedAction = \ _tcf (_progs, _projdir, _ver) _ma -> do+ runCHQuery $ do+ q <- join7+ <$> ghcOptions+ <*> ghcPkgOptions+ <*> ghcSrcOptions+ <*> ghcLangOptions+ <*> entrypoints+ <*> entrypoints+ <*> sourceDirs+ let cs = flip map q $ curry8 (GmComponent mempty)+ return ([setupConfigPath distdir], cs)+ }+ where+ curry8 fn (a, (b, (c, (d, (e, (f, (g, h))))))) = fn a b c d e f g h++ join7 a b c d e f = join' a . join' b . join' c . join' d . join' e . join' f+ join' :: Eq a => [(a,b)] -> [(a,c)] -> [(a,(b,c))]+ join' lb lc = [ (a, (b, c))+ | (a, b) <- lb+ , (a', c) <- lc+ , a == a'+ ]++getQueryEnv :: (IOish m, GmOut m, GmEnv m) => m QueryEnv+getQueryEnv = do+ crdl <- cradle+ progs <- patchStackPrograms crdl =<< (optPrograms <$> options)+ readProc <- gmReadProcess+ let projdir = cradleRootDir crdl+ distdir = projdir </> cradleDistDir crdl+ return (defaultQueryEnv projdir distdir) {+ qeReadProcess = readProc+ , qePrograms = helperProgs progs+ }++runCHQuery :: (IOish m, GmOut m, GmEnv m) => Query m b -> m b+runCHQuery a = do+ qe <- getQueryEnv+ runQuery qe a+++prepareCabalHelper :: (IOish m, GmEnv m, GmOut m, GmLog m) => m ()+prepareCabalHelper = do+ crdl <- cradle+ when (isCabalHelperProject $ cradleProject crdl) $+ withCabal $ prepare' =<< getQueryEnv++withAutogen :: (IOish m, GmEnv m, GmOut m, GmLog m) => m a -> m a+withAutogen action = do+ gmLog GmDebug "" $ strDoc $ "making sure autogen files exist"+ crdl <- cradle+ let projdir = cradleRootDir crdl+ distdir = projdir </> cradleDistDir crdl++ (pkgName', _) <- runCHQuery packageId++ mCabalFile <- liftIO $ timeFile `traverse` cradleCabalFile crdl+ mCabalMacroHeader <- liftIO $ timeMaybe (distdir </> macrosHeaderPath)+ mCabalPathsModule <- liftIO $ timeMaybe (distdir </> autogenModulePath pkgName')++ when (mCabalMacroHeader < mCabalFile || mCabalPathsModule < mCabalFile) $ do+ gmLog GmDebug "" $ strDoc $ "autogen files out of sync"+ writeAutogen++ action++ where+ writeAutogen = do+ gmLog GmDebug "" $ strDoc $ "writing Cabal autogen files"+ writeAutogenFiles' =<< getQueryEnv+++withCabal :: (IOish m, GmEnv m, GmOut m, GmLog m) => m a -> m a+withCabal action = do+ crdl <- cradle+ mCabalFile <- liftIO $ timeFile `traverse` cradleCabalFile crdl+ mCabalConfig <- liftIO $ timeMaybe (setupConfigFile crdl)+ mCabalSandboxConfig <- liftIO $ timeMaybe (sandboxConfigFile crdl)++ let haveSetupConfig = isJust mCabalConfig++ cusPkgDb <- getCustomPkgDbStack+ (flgs, pkgDbStackOutOfSync) <- do+ if haveSetupConfig+ then runCHQuery $ do+ flgs <- nonDefaultConfigFlags+ pkgDb <- map chPkgToGhcPkg <$> packageDbStack+ return (flgs, fromMaybe False $ (pkgDb /=) <$> cusPkgDb)+ else return ([], False)++ when (isSetupConfigOutOfDate mCabalFile mCabalConfig) $+ gmLog GmDebug "" $ strDoc $ "setup configuration is out of date"++ when (isSetupConfigOutOfDate mCabalSandboxConfig mCabalConfig) $+ gmLog GmDebug "" $ strDoc $ "sandbox configuration is out of date"++ when pkgDbStackOutOfSync $+ gmLog GmDebug "" $ strDoc $ "package-db stack out of sync with ghc-mod.package-db-stack"++ when ( isSetupConfigOutOfDate mCabalFile mCabalConfig+ || pkgDbStackOutOfSync+ || isSetupConfigOutOfDate mCabalSandboxConfig mCabalConfig) $ do+ proj <- cradleProject <$> cradle+ opts <- options+ case proj of+ CabalProject -> do+ gmLog GmDebug "" $ strDoc "reconfiguring Cabal project"+ cabalReconfigure (optPrograms opts) crdl flgs+ StackProject {} -> do+ gmLog GmDebug "" $ strDoc "reconfiguring Stack project"+ -- TODO: we could support flags for stack too, but it seems+ -- you're supposed to put those in stack.yaml so detecting which+ -- flags to pass down would be more difficult++ -- "--flag PACKAGE:[-]FLAG Override flags set in stack.yaml+ -- (applies to local packages and extra-deps)"++ stackReconfigure (optStackBuildDeps opts) crdl (optPrograms opts)+ _ ->+ error $ "withCabal: unsupported project type: " ++ show proj++ action++ where+ cabalReconfigure progs crdl flgs = do+ readProc <- gmReadProcess+ withDirectory_ (cradleRootDir crdl) $ do+ cusPkgStack <- maybe [] ((PackageDb "clear"):) <$> getCustomPkgDbStack+ let progOpts =+ [ "--with-ghc=" ++ T.ghcProgram progs ]+ -- Only pass ghc-pkg if it was actually set otherwise we+ -- might break cabal's guessing logic+ ++ if T.ghcPkgProgram progs /= T.ghcPkgProgram (optPrograms defaultOptions)+ then [ "--with-ghc-pkg=" ++ T.ghcPkgProgram progs ]+ else []+ ++ map pkgDbArg cusPkgStack+ ++ flagOpt++ toFlag (f, True) = f+ toFlag (f, False) = '-':f+ flagOpt = ["--flags", unwords $ map toFlag flgs]++ liftIO $ void $ readProc (T.cabalProgram progs) ("configure":progOpts) ""+ stackReconfigure deps crdl progs = do+ withDirectory_ (cradleRootDir crdl) $ do+ supported <- haveStackSupport+ if supported+ then do+ when deps $+ spawn [T.stackProgram progs, "build", "--only-dependencies", "."]+ spawn [T.stackProgram progs, "build", "--only-configure", "."]+ else+ gmLog GmWarning "" $ strDoc $ "Stack project configuration is out of date, please reconfigure manually using 'stack build' as your stack version is too old (need at least 0.1.4.0)"++ spawn [] = return ()+ spawn (exe:args) = do+ readProc <- gmReadProcess+ liftIO $ void $ readProc exe args ""++ haveStackSupport = do+ (rv, _, _) <-+ liftIO $ readProcessWithExitCode "stack" ["--numeric-version"] ""+ case rv of+ ExitSuccess -> return True+ ExitFailure _ -> return False++++pkgDbArg :: GhcPkgDb -> String+pkgDbArg GlobalDb = "--package-db=global"+pkgDbArg UserDb = "--package-db=user"+pkgDbArg (PackageDb p) = "--package-db=" ++ p++-- * Neither file exists -> should return False:+-- @Nothing < Nothing = False@+-- (since we don't need to @cabal configure@ when no cabal file exists.)+--+-- * Cabal file doesn't exist (impossible since cabal-helper is only used with+-- cabal projects) -> should return False+-- @Just cc < Nothing = False@+--+-- * dist/setup-config doesn't exist yet -> should return True:+-- @Nothing < Just cf = True@+--+-- * Both files exist+-- @Just cc < Just cf = cc < cf = cc `olderThan` cf@+isSetupConfigOutOfDate :: Maybe TimedFile -> Maybe TimedFile -> Bool+isSetupConfigOutOfDate worldCabalFile worldCabalConfig = do+ worldCabalConfig < worldCabalFile++helperProgs :: Programs -> CH.Programs+helperProgs progs = CH.Programs {+ cabalProgram = T.cabalProgram progs,+ ghcProgram = T.ghcProgram progs,+ ghcPkgProgram = T.ghcPkgProgram progs+ }++chCached :: (Applicative m, IOish m, Gm m, Binary a)+ => (FilePath -> Cached m GhcModState ChCacheData a) -> m a+chCached c = do+ projdir <- cradleRootDir <$> cradle+ distdir <- (projdir </>) . cradleDistDir <$> cradle+ d <- cacheInputData projdir+ withCabal $ cached projdir (c distdir) d+ where+ -- we don't need to include the distdir in the cache input because when it+ -- changes the cache files will be gone anyways ;)+ cacheInputData projdir = do+ opts <- options+ crdl <- cradle+ progs' <- patchStackPrograms crdl (optPrograms opts)+ return $ ( helperProgs progs'+ , projdir+ , (showVersion gmVer, chVer)+ )++ gmVer = GhcMod.version+ chVer = VERSION_cabal_helper
+ core/GhcMod/Caching.hs view
@@ -0,0 +1,141 @@+-- ghc-mod: Happy Haskell Hacking+-- Copyright (C) 2015 Daniel Gröber <dxld ÄT darkboxed DOT org>+--+-- This program is free software: you can redistribute it and/or modify+-- it under the terms of the GNU Affero General Public License as published by+-- the Free Software Foundation, either version 3 of the License, or+-- (at your option) any later version.+--+-- This program is distributed in the hope that it will be useful,+-- but WITHOUT ANY WARRANTY; without even the implied warranty of+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+-- GNU Affero General Public License for more details.+--+-- You should have received a copy of the GNU Affero General Public License+-- along with this program. If not, see <http://www.gnu.org/licenses/>.+{-# LANGUAGE CPP, OverloadedStrings #-}+module GhcMod.Caching (+ module GhcMod.Caching+ , module GhcMod.Caching.Types+ ) where++import Control.Arrow (first)+import Control.Monad+import Control.Monad.Trans.Maybe+#if !MIN_VERSION_binary(0,7,0)+import Control.Exception+#endif+import Data.Maybe+import Data.Binary hiding (get)+import Data.Version+import Data.Label+import qualified Data.ByteString.Lazy as LBS+import qualified Data.ByteString as BS+import qualified Data.ByteString.Char8 as BS8+import System.FilePath+import System.Directory.ModTime+import Utils (TimedFile(..), timeMaybe, mightExist)+import Paths_ghc_mod (version)+import Prelude++import GhcMod.Monad.Types+import GhcMod.Caching.Types+import GhcMod.Logging++-- | Cache a MonadIO action with proper invalidation.+cached :: forall m a d. (Gm m, MonadIO m, Binary a, Eq d, Binary d, Show d)+ => FilePath -- ^ Directory to prepend to 'cacheFile'+ -> Cached m GhcModState d a -- ^ Cache descriptor+ -> d+ -> m a+cached dir cd d = do+ mcc <- readCache+ case mcc of+ Nothing -> do+ t <- liftIO $ getCurrentModTime+ writeCache (TimedCacheFiles t []) Nothing "cache missing or unreadable"+ Just (t, ifs, d', a) | d /= d' -> do+ tcfs <- timeCacheInput dir ifs+ writeCache (TimedCacheFiles t tcfs) (Just a) $ "input data changed" -- ++ " was: " ++ show d ++ " is: " ++ show d'+ Just (t, ifs, _, a) -> do+ tcfs <- timeCacheInput dir ifs+ case invalidatingInputFiles $ TimedCacheFiles t tcfs of+ [] -> return a+ _ -> writeCache (TimedCacheFiles t tcfs) (Just a) "input files changed"++ where+ cacheHeader = BS8.pack $ "Written by ghc-mod " ++ showVersion version ++ "\n"++ lbsToStrict = BS.concat . LBS.toChunks+ lbsFromStrict bs = LBS.fromChunks [bs]++ writeCache tcfs ma cause = do+ (ifs', a) <- (cachedAction cd) tcfs d ma+ t <- liftIO $ getCurrentModTime+ gmLog GmDebug "" $ (text "regenerating cache") <+>: text (cacheFile cd)+ <+> parens (text cause)+ case cacheLens cd of+ Nothing -> return ()+ Just label -> do+ gmLog GmDebug "" $ (text "writing memory cache") <+>: text (cacheFile cd)+ setLabel label $ Just (t, ifs', d, a)++ let c = BS.append cacheHeader $ lbsToStrict $ encode (t, ifs', d, a)++ liftIO $ BS.writeFile (dir </> cacheFile cd) c++ return a++ setLabel l x = do+ s <- gmsGet+ gmsPut $ set l x s++ readCache :: m (Maybe (ModTime, [FilePath], d, a))+ readCache = runMaybeT $ do+ case cacheLens cd of+ Just label -> do+ c <- MaybeT (get label `liftM` gmsGet) `mplus` readCacheFromFile+ setLabel label $ Just c+ return c+ Nothing ->+ readCacheFromFile++ readCacheFromFile :: MaybeT m (ModTime, [FilePath], d, a)+ readCacheFromFile = do+ f <- MaybeT $ liftIO $ mightExist $ cacheFile cd+ readCacheFromFile' f++ readCacheFromFile' :: FilePath -> MaybeT m (ModTime, [FilePath], d, a)+ readCacheFromFile' f = MaybeT $ do+ gmLog GmDebug "" $ (text "reading cache") <+>: text (cacheFile cd)+ cc <- liftIO $ BS.readFile f+ case first BS8.words $ BS8.span (/='\n') cc of+ (["Written", "by", "ghc-mod", ver], rest)+ | BS8.unpack ver == showVersion version ->+ either (const Nothing) Just+ `liftM` decodeE (lbsFromStrict $ BS.drop 1 rest)+ _ -> return Nothing++ decodeE b = do+#if MIN_VERSION_binary(0,7,0)+ return $ case decodeOrFail b of+ Left (_rest, _offset, errmsg) -> Left errmsg+ Right (_reset, _offset, a) -> Right a+#else+ ea <- liftIO $ try $ evaluate $ decode b+ return $ case ea of+ Left (ErrorCall errmsg) -> Left errmsg+ Right a -> Right a++#endif++timeCacheInput :: MonadIO m => FilePath -> [FilePath] -> m [TimedFile]+timeCacheInput dir ifs = liftIO $ do+ ins <- (timeMaybe . (dir </>)) `mapM` ifs+ return $ catMaybes ins++invalidatingInputFiles :: TimedCacheFiles -> [FilePath]+invalidatingInputFiles (TimedCacheFiles tcreated tcfs) =+ map tfPath $+ -- get input files older than tcfile+ filter ((TimedFile "" tcreated)<) tcfs
+ core/GhcMod/Caching/Types.hs view
@@ -0,0 +1,67 @@+-- ghc-mod: Happy Haskell Hacking+-- Copyright (C) 2015 Daniel Gröber <dxld ÄT darkboxed DOT org>+--+-- This program is free software: you can redistribute it and/or modify+-- it under the terms of the GNU Affero General Public License as published by+-- the Free Software Foundation, either version 3 of the License, or+-- (at your option) any later version.+--+-- This program is distributed in the hope that it will be useful,+-- but WITHOUT ANY WARRANTY; without even the implied warranty of+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+-- GNU Affero General Public License for more details.+--+-- You should have received a copy of the GNU Affero General Public License+-- along with this program. If not, see <http://www.gnu.org/licenses/>.+module GhcMod.Caching.Types where++import Utils+import Data.Label+import System.Directory.ModTime+import Distribution.Helper++type CacheContents d a = Maybe (ModTime, [FilePath], d, a)+type CacheLens s d a = s :-> CacheContents d a++data Cached m s d a = Cached {+ cacheFile :: FilePath,+ cacheLens :: Maybe (CacheLens s d a),+ cachedAction :: TimedCacheFiles+ -> d+ -> Maybe a+ -> m ([FilePath], a)++ -- ^ @cachedAction tcf data ma@+ --+ -- * @tcf@: Input file timestamps. Not technically necessary, just an+ -- optimizazion when knowing which input files changed can make updating the+ -- cache faster+ --+ -- * @data@: Arbitrary static input data to cache action. Can be used to+ -- invalidate the cache using something other than file timestamps+ -- i.e. environment tool version numbers+ --+ -- * @ma@: Cached data if it existed+ --+ -- Returns:+ --+ -- * @fst@: Input files used in generating the cache+ --+ -- * @snd@: Cache data, will be stored alongside the static input data in the+ -- 'cacheFile'+ --+ -- The cached action, will only run if one of the following is true:+ --+ -- * 'cacheFile' doesn\'t exist yet+ -- * 'cacheFile' exists and 'inputData' changed+ -- * any files returned by the cached action changed+ }++data TimedCacheFiles = TimedCacheFiles {+ tcCreated :: ModTime,+ -- ^ 'cacheFile' timestamp+ tcFiles :: [TimedFile]+ -- ^ Timestamped files returned by the cached action+ } deriving (Eq, Ord)++type ChCacheData = (Programs, FilePath, (String, String))
+ core/GhcMod/Convert.hs view
@@ -0,0 +1,140 @@+{-# LANGUAGE CPP, FlexibleInstances, FlexibleContexts #-}++module GhcMod.Convert (convert, convert', emptyResult, whenFound, whenFound') where++import GhcMod.Monad.Types+import GhcMod.Types++import Control.Applicative+import Prelude++type Builder = String -> String++-- |+--+-- >>> replace '"' "\\\"" "foo\"bar" ""+-- "foo\\\"bar"+replace :: Char -> String -> String -> Builder+replace _ _ [] = id+replace c cs (x:xs)+ | x == c = (cs ++) . replace c cs xs+ | otherwise = (x :) . replace c cs xs++inter :: Char -> [Builder] -> Builder+inter _ [] = id+inter c bs = foldr1 (\x y -> x . (c:) . y) bs++convert' :: (ToString a, IOish m, GmEnv m) => a -> m String+convert' x = flip convert x . optOutput <$> options++convert :: ToString a => OutputOpts -> a -> String+convert opt@OutputOpts { ooptStyle = LispStyle } x = toLisp opt x "\n"+convert opt@OutputOpts { ooptStyle = PlainStyle } x+ | str == "\n" = ""+ | otherwise = str+ where+ str = toPlain opt x "\n"++class ToString a where+ toLisp :: OutputOpts -> a -> Builder+ toPlain :: OutputOpts -> a -> Builder++lineSep :: OutputOpts -> String+lineSep oopts = interpret lsep+ where+ interpret s = read $ "\"" ++ s ++ "\""+ LineSeparator lsep = ooptLineSeparator oopts++-- |+--+-- >>> toLisp (optOutput defaultOptions) "fo\"o" ""+-- "\"fo\\\"o\""+-- >>> toPlain (optOutput defaultOptions) "foo" ""+-- "foo"+instance ToString String where+ toLisp oopts = quote oopts+ toPlain oopts = replace '\n' (lineSep oopts)++-- |+--+-- >>> toLisp (optOutput defaultOptions) ["foo", "bar", "ba\"z"] ""+-- "(\"foo\" \"bar\" \"ba\\\"z\")"+-- >>> toPlain (optOutput defaultOptions) ["foo", "bar", "baz"] ""+-- "foo\nbar\nbaz"+instance ToString [String] where+ toLisp oopts = toSexp1 oopts+ toPlain oopts = inter '\n' . map (toPlain oopts)++instance ToString [ModuleString] where+ toLisp oopts = toLisp oopts . map getModuleString+ toPlain oopts = toPlain oopts . map getModuleString++-- |+--+-- >>> let inp = [((1,2,3,4),"foo"),((5,6,7,8),"bar")] :: [((Int,Int,Int,Int),String)]+-- >>> toLisp (optOutput defaultOptions) inp ""+-- "((1 2 3 4 \"foo\") (5 6 7 8 \"bar\"))"+-- >>> toPlain (optOutput defaultOptions) inp ""+-- "1 2 3 4 \"foo\"\n5 6 7 8 \"bar\""+instance ToString [((Int,Int,Int,Int),String)] where+ toLisp oopts = toSexp2 . map toS+ where+ toS x = ('(' :) . tupToString oopts x . (')' :)+ toPlain oopts = inter '\n' . map (tupToString oopts)++instance ToString ((Int,Int,Int,Int),String) where+ toLisp oopts x = ('(' :) . tupToString oopts x . (')' :)+ toPlain oopts x = tupToString oopts x++instance ToString ((Int,Int,Int,Int),[String]) where+ toLisp oopts (x,s) = ('(' :) . fourIntsToString x .+ (' ' :) . toLisp oopts s . (')' :)+ toPlain oopts (x,s) = fourIntsToString x . ('\n' :) . toPlain oopts s++instance ToString (String, (Int,Int,Int,Int),[String]) where+ toLisp oopts (s,x,y) = toSexp2 [toLisp oopts s, ('(' :) . fourIntsToString x . (')' :), toLisp oopts y]+ toPlain oopts (s,x,y) = inter '\n' [toPlain oopts s, fourIntsToString x, toPlain oopts y]++toSexp1 :: OutputOpts -> [String] -> Builder+toSexp1 oopts ss = ('(' :) . inter ' ' (map (quote oopts) ss) . (')' :)++toSexp2 :: [Builder] -> Builder+toSexp2 ss = ('(' :) . inter ' ' ss . (')' :)++fourIntsToString :: (Int,Int,Int,Int) -> Builder+fourIntsToString (a,b,c,d) = (show a ++) . (' ' :)+ . (show b ++) . (' ' :)+ . (show c ++) . (' ' :)+ . (show d ++)++tupToString :: OutputOpts -> ((Int,Int,Int,Int),String) -> Builder+tupToString oopts ((a,b,c,d),s) = (show a ++) . (' ' :)+ . (show b ++) . (' ' :)+ . (show c ++) . (' ' :)+ . (show d ++) . (' ' :)+ . quote oopts s -- fixme: quote is not necessary++quote :: OutputOpts -> String -> Builder+quote oopts str = ("\"" ++) . (quote' str ++) . ("\"" ++)+ where+ lsep = lineSep oopts+ quote' [] = []+ quote' (x:xs)+ | x == '\n' = lsep ++ quote' xs+ | x == '\\' = "\\\\" ++ quote' xs+ | x == '"' = "\\\"" ++ quote' xs+ | otherwise = x : quote' xs++----------------------------------------------------------------++-- Empty result to be returned when no info can be gathered+emptyResult :: Monad m => OutputOpts -> m String+emptyResult oopts = return $ convert oopts ([] :: [String])++-- Return an emptyResult when Nothing+whenFound :: (Monad m, ToString b) => OutputOpts -> m (Maybe a) -> (a -> b) -> m String+whenFound oopts from f = maybe (emptyResult oopts) (return . convert oopts . f) =<< from++-- Return an emptyResult when Nothing, inside a monad+whenFound' :: (Monad m, ToString b) => OutputOpts -> m (Maybe a) -> (a -> m b) -> m String+whenFound' oopts from f = maybe (emptyResult oopts) (\x -> do y <- f x ; return (convert oopts y)) =<< from
+ core/GhcMod/Cradle.hs view
@@ -0,0 +1,185 @@+{-# LANGUAGE CPP #-}+module GhcMod.Cradle+ ( findCradle+ , findCradle'+ , findCradleNoLog+ , findSpecCradle+ , cleanupCradle++ -- * for @spec@+ , plainCradle+ ) where++import GhcMod.PathsAndFiles+import GhcMod.Monad.Types+import GhcMod.Types+import GhcMod.Utils+import GhcMod.Stack+import GhcMod.Logging+import GhcMod.Error++import Safe+import Control.Applicative+import Control.Monad.Trans.Maybe+import Data.Maybe+import System.Directory+import System.FilePath+import System.Environment+import Prelude+import Control.Monad.Trans.Journal (runJournalT)++----------------------------------------------------------------++-- | Finding 'Cradle'.+-- Find a cabal file by tracing ancestor directories.+-- Find a sandbox according to a cabal sandbox config+-- in a cabal directory.+findCradle :: (GmLog m, IOish m, GmOut m) => Programs -> m Cradle+findCradle progs = findCradle' progs =<< liftIO getCurrentDirectory++findCradleNoLog :: forall m. (IOish m, GmOut m) => Programs -> m Cradle+findCradleNoLog progs =+ fst <$> (runJournalT (findCradle progs) :: m (Cradle, GhcModLog))++findCradle' :: (GmLog m, IOish m, GmOut m) => Programs -> FilePath -> m Cradle+findCradle' Programs { stackProgram, cabalProgram } dir = run $+ msum [ stackCradle stackProgram dir+ , cabalCradle cabalProgram dir+ , sandboxCradle dir+ , plainCradle dir+ ]+ where run a = fillTempDir =<< (fromJustNote "findCradle'" <$> runMaybeT a)++findSpecCradle ::+ (GmLog m, IOish m, GmOut m) => Programs -> FilePath -> m Cradle+findSpecCradle Programs { stackProgram, cabalProgram } dir = do+ let cfs = [ stackCradleSpec stackProgram+ , cabalCradle cabalProgram+ , sandboxCradle+ ]+ cs <- catMaybes <$> mapM (runMaybeT . ($ dir)) cfs+ gcs <- filterM isNotGmCradle cs+ fillTempDir =<< case gcs of+ [] -> fromJust <$> runMaybeT (plainCradle dir)+ c:_ -> return c+ where+ isNotGmCradle crdl =+ liftIO $ not <$> doesFileExist (cradleRootDir crdl </> "ghc-mod.cabal")++cleanupCradle :: Cradle -> IO ()+cleanupCradle crdl = removeDirectoryRecursive $ cradleTempDir crdl++fillTempDir :: IOish m => Cradle -> m Cradle+fillTempDir crdl = do+ tmpDir <- liftIO $ newTempDir (cradleRootDir crdl)+ return crdl { cradleTempDir = tmpDir }++cabalCradle ::+ (IOish m, GmLog m, GmOut m) => FilePath -> FilePath -> MaybeT m Cradle+cabalCradle cabalProg wdir = do+ cabalFile <- MaybeT $ liftIO $ findCabalFile wdir+ let cabalDir = takeDirectory cabalFile++ gmLog GmInfo "" $ text "Found Cabal project at" <+>: text cabalDir++ -- If cabal doesn't exist the user probably wants to use something else+ whenM ((==Nothing) <$> liftIO (findExecutable cabalProg)) $ do+ gmLog GmInfo "" $ text "'cabal' executable wasn't found, trying next project type"+ mzero++ gmLog GmInfo "" $ text "Using Cabal project at" <+>: text cabalDir+ return Cradle {+ cradleProject = CabalProject+ , cradleCurrentDir = wdir+ , cradleRootDir = cabalDir+ , cradleTempDir = error "tmpDir"+ , cradleCabalFile = Just cabalFile+ , cradleDistDir = "dist"+ }++stackCradle ::+ (IOish m, GmLog m, GmOut m) => FilePath -> FilePath -> MaybeT m Cradle+stackCradle stackProg wdir = do+#if __GLASGOW_HASKELL__ < 708+ -- GHC < 7.8 is not supported by stack+ mzero+#endif++ cabalFile <- MaybeT $ liftIO $ findCabalFile wdir+ let cabalDir = takeDirectory cabalFile+ _stackConfigFile <- MaybeT $ liftIO $ findStackConfigFile cabalDir++ gmLog GmInfo "" $ text "Found Stack project at" <+>: text cabalDir++ stackExeSet <- liftIO $ isJust <$> lookupEnv "STACK_EXE"+ stackExeExists <- liftIO $ isJust <$> findExecutable stackProg+ setupCfgExists <- liftIO $ doesFileExist $ cabalDir </> setupConfigPath "dist"++ case (stackExeExists, stackExeSet) of+ (False, True) -> do+ gmLog GmWarning "" $ text "'stack' executable wasn't found but STACK_EXE is set, trying next project type"+ mzero++ (False, False) -> do+ gmLog GmInfo "" $ text "'stack' executable wasn't found, trying next project type"+ mzero++ (True, True) -> do+ gmLog GmInfo "" $ text "STACK_EXE set, preferring Stack project"++ (True, False) | setupCfgExists -> do+ gmLog GmWarning "" $ text "'dist/setup-config' exists, ignoring Stack project"+ mzero++ (True, False) -> return ()++ senv <- MaybeT $ getStackEnv cabalDir stackProg++ gmLog GmInfo "" $ text "Using Stack project at" <+>: text cabalDir+ return Cradle {+ cradleProject = StackProject senv+ , cradleCurrentDir = wdir+ , cradleRootDir = cabalDir+ , cradleTempDir = error "tmpDir"+ , cradleCabalFile = Just cabalFile+ , cradleDistDir = seDistDir senv+ }++stackCradleSpec ::+ (IOish m, GmLog m, GmOut m) => FilePath -> FilePath -> MaybeT m Cradle+stackCradleSpec stackProg wdir = do+ crdl <- stackCradle stackProg wdir+ case crdl of+ Cradle { cradleProject = StackProject StackEnv { seDistDir } } -> do+ b <- isGmDistDir seDistDir+ when b mzero+ return crdl+ _ -> error "stackCradleSpec"+ where+ isGmDistDir dir =+ liftIO $ not <$> doesFileExist (dir </> ".." </> "ghc-mod.cabal")++sandboxCradle :: (IOish m, GmLog m, GmOut m) => FilePath -> MaybeT m Cradle+sandboxCradle wdir = do+ sbDir <- MaybeT $ liftIO $ findCabalSandboxDir wdir+ gmLog GmInfo "" $ text "Using sandbox project at" <+>: text sbDir+ return Cradle {+ cradleProject = SandboxProject+ , cradleCurrentDir = wdir+ , cradleRootDir = sbDir+ , cradleTempDir = error "tmpDir"+ , cradleCabalFile = Nothing+ , cradleDistDir = "dist"+ }++plainCradle :: (IOish m, GmLog m, GmOut m) => FilePath -> MaybeT m Cradle+plainCradle wdir = do+ gmLog GmInfo "" $ text "Found no other project type, falling back to plain GHC project"+ return $ Cradle {+ cradleProject = PlainProject+ , cradleCurrentDir = wdir+ , cradleRootDir = wdir+ , cradleTempDir = error "tmpDir"+ , cradleCabalFile = Nothing+ , cradleDistDir = "dist"+ }
+ core/GhcMod/CustomPackageDb.hs view
@@ -0,0 +1,38 @@+-- ghc-mod: Happy Haskell Hacking+-- Copyright (C) 2015 Daniel Gröber <dxld ÄT darkboxed DOT org>+--+-- This program is free software: you can redistribute it and/or modify+-- it under the terms of the GNU Affero General Public License as published by+-- the Free Software Foundation, either version 3 of the License, or+-- (at your option) any later version.+--+-- This program is distributed in the hope that it will be useful,+-- but WITHOUT ANY WARRANTY; without even the implied warranty of+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+-- GNU Affero General Public License for more details.+--+-- You should have received a copy of the GNU Affero General Public License+-- along with this program. If not, see <http://www.gnu.org/licenses/>.+module GhcMod.CustomPackageDb where++import Control.Applicative+import Control.Monad+import Control.Category ((.))+import Data.Maybe+import Data.Traversable+import GhcMod.Types+import GhcMod.Monad.Types+import GhcMod.PathsAndFiles+import Prelude hiding ((.))++parseCustomPackageDb :: String -> [GhcPkgDb]+parseCustomPackageDb src = map parsePkgDb $ filter (not . null) $ lines src+ where+ parsePkgDb "global" = GlobalDb+ parsePkgDb "user" = UserDb+ parsePkgDb s = PackageDb s++getCustomPkgDbStack :: (MonadIO m, GmEnv m) => m (Maybe [GhcPkgDb])+getCustomPkgDbStack = do+ mCusPkgDbFile <- liftIO . (traverse readFile <=< findCustomPackageDbFile) . cradleRootDir =<< cradle+ return $ parseCustomPackageDb <$> mCusPkgDbFile
+ core/GhcMod/DebugLogger.hs view
@@ -0,0 +1,138 @@+-- ghc-mod: Happy Haskell Hacking+-- Copyright (C) 2015 Daniel Gröber <dxld ÄT darkboxed DOT org>+--+-- This program is free software: you can redistribute it and/or modify+-- it under the terms of the GNU Affero General Public License as published by+-- the Free Software Foundation, either version 3 of the License, or+-- (at your option) any later version.+--+-- This program is distributed in the hope that it will be useful,+-- but WITHOUT ANY WARRANTY; without even the implied warranty of+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+-- GNU Affero General Public License for more details.+--+-- You should have received a copy of the GNU Affero General Public License+-- along with this program. If not, see <http://www.gnu.org/licenses/>.+{-# LANGUAGE CPP, RankNTypes #-}+module GhcMod.DebugLogger where++-- (c) The University of Glasgow 2005+--+-- The Glasgow Haskell Compiler License+--+-- Copyright 2002, The University Court of the University of Glasgow.+-- All rights reserved.+--+-- Redistribution and use in source and binary forms, with or without+-- modification, are permitted provided that the following conditions are met:+--+-- - Redistributions of source code must retain the above copyright notice,+-- this list of conditions and the following disclaimer.+--+-- - Redistributions in binary form must reproduce the above copyright notice,+-- this list of conditions and the following disclaimer in the documentation+-- and/or other materials provided with the distribution.+--+-- - Neither name of the University nor the names of its contributors may be+-- used to endorse or promote products derived from this software without+-- specific prior written permission.+--+-- THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY COURT OF THE UNIVERSITY OF+-- GLASGOW AND THE CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,+-- INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND+-- FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE+-- UNIVERSITY COURT OF THE UNIVERSITY OF GLASGOW OR THE CONTRIBUTORS BE LIABLE+-- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL+-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR+-- SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER+-- CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT+-- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY+-- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH+-- DAMAGE.++import GHC+import FastString+import Pretty+import Outputable (SDoc, PprStyle, runSDoc, initSDocContext, blankLine)+import qualified Outputable+import ErrUtils++import GhcMod.Error+import GhcMod.Gap+import Prelude++debugLogAction :: (String -> IO ()) -> GmLogAction+debugLogAction putErr _reason dflags severity srcSpan style' msg+ = case severity of+ SevOutput -> printSDoc putErr msg style'++#if __GLASGOW_HASKELL__ >= 706+ SevDump -> printSDoc putErr (msg Outputable.$$ blankLine) style'+#endif++#if __GLASGOW_HASKELL__ >= 708+ SevInteractive -> let+ putStrSDoc = debugLogActionHPutStrDoc dflags putErr+ in+ putStrSDoc msg style'+#endif+ SevInfo -> printErrs putErr msg style'+ SevFatal -> printErrs putErr msg style'+ _ -> do putErr "\n"+#if __GLASGOW_HASKELL__ >= 706+ printErrs putErr (mkLocMessage severity srcSpan msg) style'+#else+ printErrs putErr (mkLocMessage srcSpan msg) style'+#endif+ -- careful (#2302): printErrs prints in UTF-8,+ -- whereas converting to string first and using+ -- hPutStr would just emit the low 8 bits of+ -- each unicode char.+ where+#if __GLASGOW_HASKELL__ >= 706+ printSDoc put = debugLogActionHPrintDoc dflags put+ printErrs put = debugLogActionHPrintDoc dflags put+#endif+++#if __GLASGOW_HASKELL__ >= 706++debugLogActionHPrintDoc :: DynFlags -> (String -> IO ()) -> SDoc -> PprStyle -> IO ()+debugLogActionHPrintDoc dflags put d sty+ = debugLogActionHPutStrDoc dflags put (d Outputable.$$ Outputable.text "") sty+ -- Adds a newline++debugLogActionHPutStrDoc :: DynFlags -> (String -> IO ()) -> SDoc -> PprStyle -> IO ()+debugLogActionHPutStrDoc dflags put d sty+ = gmPrintDoc_ Pretty.PageMode (pprCols dflags) put doc+ where -- Don't add a newline at the end, so that successive+ -- calls to this log-action can output all on the same line+ doc = runSDoc d (initSDocContext dflags sty)++#else++printSDoc = printErrs++printErrs :: (String -> IO ()) -> SDoc -> PprStyle -> IO ()+printErrs put doc sty = do+ gmPrintDoc PageMode 100 put (runSDoc doc (initSDocContext sty))++#endif++gmPrintDoc :: Mode -> Int -> (String -> IO ()) -> Doc -> IO ()+-- printDoc adds a newline to the end+gmPrintDoc mode cols put doc = gmPrintDoc_ mode cols put (doc $$ text "")++gmPrintDoc_ :: Mode -> Int -> (String -> IO ()) -> Doc -> IO ()+gmPrintDoc_ mode pprCols putS doc+ = fullRender mode pprCols 1.5 put done doc+ where+ put (Chr c) next = putS [c] >> next+ put (Str s) next = putS s >> next+ put (PStr s) next = putS (unpackFS s) >> next+#if __GLASGOW_HASKELL__ >= 708+ put (ZStr s) next = putS (zString s) >> next+#endif+ put (LStr s _l) next = putS (unpackLitString s) >> next++ done = return () -- hPutChar hdl '\n'
+ core/GhcMod/Doc.hs view
@@ -0,0 +1,20 @@+module GhcMod.Doc where++import GHC+import GhcMod.Gap (withStyle, showDocWith)+import Outputable+import Pretty (Mode(..))++showPage :: DynFlags -> PprStyle -> SDoc -> String+showPage dflag style = showDocWith dflag PageMode . withStyle dflag style++showOneLine :: DynFlags -> PprStyle -> SDoc -> String+showOneLine dflag style = showDocWith dflag OneLineMode . withStyle dflag style++getStyle :: GhcMonad m => m PprStyle+getStyle = do+ unqual <- getPrintUnqual+ return $ mkUserStyle unqual AllTheWay++styleUnqualified :: PprStyle+styleUnqualified = mkUserStyle neverQualify AllTheWay
+ core/GhcMod/DynFlags.hs view
@@ -0,0 +1,108 @@+{-# LANGUAGE TemplateHaskell #-}++module GhcMod.DynFlags where++import Control.Applicative+import Control.Monad+import GHC+import qualified GHC as G+import GHC.Paths (libdir)+import qualified GhcMod.Gap as Gap+import GhcMod.Types+import GhcMod.DebugLogger+import GhcMod.DynFlagsTH+import System.IO.Unsafe (unsafePerformIO)+import Prelude++setEmptyLogger :: DynFlags -> DynFlags+setEmptyLogger df =+ Gap.setLogAction df $ \_ _ _ _ _ _ -> return ()++setDebugLogger :: (String -> IO ()) -> DynFlags -> DynFlags+setDebugLogger put df = do+ Gap.setLogAction df (debugLogAction put)++-- * Fast+-- * Friendly to foreign export+-- * Not friendly to -XTemplateHaskell and -XPatternSynonyms+-- * Uses little memory+setHscNothing :: DynFlags -> DynFlags+setHscNothing df = df {+ ghcMode = CompManager+ , ghcLink = NoLink+ , hscTarget = HscNothing+ , optLevel = 0+ }++-- * Slow+-- * Not friendly to foreign export+-- * Friendly to -XTemplateHaskell and -XPatternSynonyms+-- * Uses lots of memory+setHscInterpreted :: DynFlags -> DynFlags+setHscInterpreted df = df {+ ghcMode = CompManager+ , ghcLink = LinkInMemory+ , hscTarget = HscInterpreted+ , optLevel = 0+ }++-- | Parse command line ghc options and add them to the 'DynFlags' passed+addCmdOpts :: GhcMonad m => [GHCOption] -> DynFlags -> m DynFlags+addCmdOpts cmdOpts df =+ fst3 <$> G.parseDynamicFlags df (map G.noLoc cmdOpts)+ where+ fst3 (a,_,_) = a++----------------------------------------------------------------++withDynFlags :: GhcMonad m+ => (DynFlags -> DynFlags)+ -> m a+ -> m a+withDynFlags setFlags body = G.gbracket setup teardown (\_ -> body)+ where+ setup = do+ dflags <- G.getSessionDynFlags+ void $ G.setSessionDynFlags (setFlags dflags)+ return dflags+ teardown = void . G.setSessionDynFlags++withCmdFlags :: GhcMonad m => [GHCOption] -> m a -> m a+withCmdFlags flags body = G.gbracket setup teardown (\_ -> body)+ where+ setup = do+ dflags <- G.getSessionDynFlags+ void $ G.setSessionDynFlags =<< addCmdOpts flags dflags+ return dflags+ teardown = void . G.setSessionDynFlags++----------------------------------------------------------------++-- | Set 'DynFlags' equivalent to "-w:".+setNoWarningFlags :: DynFlags -> DynFlags+setNoWarningFlags df = df { warningFlags = Gap.emptyWarnFlags}++-- | Set 'DynFlags' equivalent to "-Wall".+setAllWarningFlags :: DynFlags -> DynFlags+setAllWarningFlags df = df { warningFlags = allWarningFlags }++allWarningFlags :: Gap.WarnFlags+allWarningFlags = unsafePerformIO $+ G.runGhc (Just libdir) $ do+ df <- G.getSessionDynFlags+ df' <- addCmdOpts ["-Wall"] df+ return $ G.warningFlags df'++----------------------------------------------------------------++deferErrors :: Monad m => DynFlags -> m DynFlags+deferErrors df = return $+ Gap.setWarnTypedHoles $ Gap.setDeferTypedHoles $+ Gap.setDeferTypeErrors $ setNoWarningFlags df++----------------------------------------------------------------++deriveEqDynFlags [d|+ eqDynFlags :: DynFlags -> DynFlags -> Bool+ eqDynFlags = undefined+ |]
+ core/GhcMod/DynFlagsTH.hs view
@@ -0,0 +1,144 @@+-- ghc-mod: Happy Haskell Hacking+-- Copyright (C) 2015 Daniel Gröber <dxld ÄT darkboxed DOT org>+--+-- This program is free software: you can redistribute it and/or modify+-- it under the terms of the GNU Affero General Public License as published by+-- the Free Software Foundation, either version 3 of the License, or+-- (at your option) any later version.+--+-- This program is distributed in the hope that it will be useful,+-- but WITHOUT ANY WARRANTY; without even the implied warranty of+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+-- GNU Affero General Public License for more details.+--+-- You should have received a copy of the GNU Affero General Public License+-- along with this program. If not, see <http://www.gnu.org/licenses/>.++{-# LANGUAGE CPP, TemplateHaskell #-}+module GhcMod.DynFlagsTH where++import Language.Haskell.TH+import Language.Haskell.TH.Syntax+import Control.Applicative+import Data.Maybe+import Data.Generics.Aliases+import Data.Generics.Schemes+import DynFlags+import Prelude++deriveEqDynFlags :: Q [Dec] -> Q [Dec]+deriveEqDynFlags qds = do+#if __GLASGOW_HASKELL__ <= 710+ ~(TyConI (DataD [] _ [] [ctor] _ ))+#else+ ~(TyConI (DataD [] _ [] _ [ctor] _ ))+#endif+ <- reify ''DynFlags+ let ~(RecC _ fs) = ctor++ a <- newName "a"+ b <- newName "b"++ e <- AppE (VarE 'and) . ListE <$> sequence (catMaybes $ map (eq a b) fs)++ tysig@(SigD n _) :_ <- qds++ return $ [tysig, FunD n [Clause [VarP a, VarP b] (NormalB e) []]]++ where+ eq :: Name -> Name -> (Name, Strict, Type) -> Maybe (Q Exp)+ eq a b (fun@(Name (OccName fon) _), _, ft)+ | not (isUneqable || isIgnored) = Just expr+ | otherwise = Nothing+ where+ isUneqable = everything (||) (mkQ False hasUnEqable) ft++ hasUnEqable (AppT (ConT (Name (OccName on) _)) _)+ | any (==on) ignoredConstructorNames = True+ hasUnEqable (ConT (Name (OccName on) _))+ | any (==on) ignoredTypeNames = True+ | any (==on) ignoredTypeOccNames = True+ hasUnEqable _ = False++ isIgnored = fon `elem` ignoredNames++ ignoredConstructorNames = [ "IORef" ]++ ignoredNames = [ "pkgDatabase" -- 7.8+#if __GLASGOW_HASKELL__ <= 706+ , "ways" -- 'Ways' is not exported :/+#endif+ ]+ ignoredTypeNames =+ [ "LogAction"+ , "PackageState"+ , "Hooks"+ , "FlushOut"+ , "FlushErr"+ , "Settings" -- I think these can't cange at runtime+ ]+ ignoredTypeOccNames = [ "OnOff" ]++ fa = AppE (VarE fun) (VarE a)+ fb = AppE (VarE fun) (VarE b)+ expr =+ case fon of+ "rtsOptsEnabled" -> do+ let eqfn = [| let fn RtsOptsNone RtsOptsNone = True+ fn RtsOptsSafeOnly RtsOptsSafeOnly = True+ fn RtsOptsAll RtsOptsAll = True+ fn _ _ = False+ in fn+ |]+ [e| $(eqfn) $(return fa) $(return fb) |]++ "extraPkgConfs" -> do+ let eqfn = [| let fn a' b' = and (zipWith eqpr (a' []) (b' []))+ && length (a' []) == length (b' [])+ eqpr GlobalPkgConf GlobalPkgConf = True+ eqpr UserPkgConf UserPkgConf = True+ eqpr (PkgConfFile pa) (PkgConfFile pb) = pa == pb+ eqpr _ _ = False+ in fn+ |]+ [e| $(eqfn) $(return fa) $(return fb) |]++#if __GLASGOW_HASKELL__ >= 710 && __GLASGOW_HASKELL__ < 800+ "sigOf" -> do+ let eqfn = [| let fn NotSigOf NotSigOf = True+ fn (SigOf a') (SigOf b') = a' == b'+ fn (SigOfMap a') (SigOfMap b') = a' == b'+ fn _ _ = False+ in fn+ |]+ [e| $(eqfn) $(return fa) $(return fb) |]+#endif++#if __GLASGOW_HASKELL <= 706+ "profAuto" -> do+ let eqfn = [| let fn NoProfAuto NoProfAuto = True+ fn ProfAutoAll ProfAutoAll = True+ fn ProfAutoTop ProfAutoTop = True+ fn ProfAutoExports ProfAutoExports = True+ fn ProfAutoCalls ProfAutoCalls = True+ fn _ _ = False+ in fn+ |]+ [e| $(eqfn) $(return fa) $(return fb) |]+#endif++#if __GLASGOW_HASKELL__ >= 706+ "language" -> do+ let eqfn = [| let fn (Just Haskell98) (Just Haskell98) = True+ fn (Just Haskell2010) (Just Haskell2010) = True+ fn Nothing Nothing = True+ fn _ _ = False+ in fn+ |]+ [e| $(eqfn) $(return fa) $(return fb) |]+#endif++ _ ->+ [e| $(return fa) == $(return fb) |]++-- expr' = [e| trace (if $(expr) == True then "" else show ($(litE $ StringL fon), $(expr))) $(expr) |]
+ core/GhcMod/Error.hs view
@@ -0,0 +1,160 @@+-- ghc-mod: Happy Haskell Hacking+-- Copyright (C) 2015 Daniel Gröber <dxld ÄT darkboxed DOT org>+--+-- This program is free software: you can redistribute it and/or modify+-- it under the terms of the GNU Affero General Public License as published by+-- the Free Software Foundation, either version 3 of the License, or+-- (at your option) any later version.+--+-- This program is distributed in the hope that it will be useful,+-- but WITHOUT ANY WARRANTY; without even the implied warranty of+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+-- GNU Affero General Public License for more details.+--+-- You should have received a copy of the GNU Affero General Public License+-- along with this program. If not, see <http://www.gnu.org/licenses/>.++{-# LANGUAGE ExistentialQuantification #-}+module GhcMod.Error (+ GhcModError(..)+ , GmError+ , gmeDoc+ , ghcExceptionDoc+ , liftMaybe+ , overrideError+ , modifyError+ , modifyError'+ , modifyGmError+ , tryFix+ , GHandler(..)+ , gcatches+ , module Control.Monad.Error+ , module Control.Exception+ ) where++import Control.Arrow hiding ((<+>))+import Control.Exception+import Control.Monad.Error hiding (MonadIO, liftIO)+import qualified Data.Set as Set+import Data.List+import Data.Version+import System.Process (showCommandForUser)+import Text.Printf++import Exception+import Panic+import Pretty+import Config (cProjectVersion, cHostPlatformString)+import Paths_ghc_mod (version)++import GhcMod.Types+import GhcMod.Pretty++type GmError m = MonadError GhcModError m++gmeDoc :: GhcModError -> Doc+gmeDoc e = case e of+ GMENoMsg ->+ text "Unknown error"+ GMEString msg ->+ text msg+ GMECabalConfigure msg ->+ text "Configuring cabal project failed" <+>: gmeDoc msg+ GMEStackConfigure msg ->+ text "Configuring stack project failed" <+>: gmeDoc msg+ GMEStackBootstrap msg ->+ text "Bootstrapping stack project environment failed" <+>: gmeDoc msg+ GMECabalCompAssignment ctx ->+ text "Could not find a consistent component assignment for modules:" $$+ (nest 4 $ foldr ($+$) empty $ map ctxDoc ctx) $$+ text "" $$+ (if all (Set.null . snd) ctx+ then noComponentSuggestions+ else empty) $$+ text "- To find out which components ghc-mod knows about try:" $$+ nest 4 (backticks $ text "ghc-mod debug")++ where+ noComponentSuggestions =+ text "- Are some of these modules part of a test and or benchmark?\+ \ Try enabling them:" $$+ nest 4 (backticks $ text "cabal configure --enable-tests [--enable-benchmarks]")++ backticks d = char '`' <> d <> char '`'+ ctxDoc = moduleDoc *** compsDoc+ >>> first (<> colon) >>> uncurry (flip hang 4)++ moduleDoc (Left fn) =+ text "File " <> quotes (text fn)+ moduleDoc (Right mdl) =+ text "Module " <> quotes (text $ moduleNameString mdl)++ compsDoc sc | Set.null sc = text "has no known components"+ compsDoc sc = fsep $ punctuate comma $+ map gmComponentNameDoc $ Set.toList sc+ GMEProcess _fn cmd args emsg -> let c = showCommandForUser cmd args in+ case emsg of+ Right err ->+ text (printf "Launching system command `%s` failed: " c)+ <> gmeDoc err+ Left rv -> text $+ printf "Launching system command `%s` failed (exited with %d)" c rv+ GMENoCabalFile ->+ text "No cabal file found."+ GMETooManyCabalFiles cfs ->+ text $ "Multiple cabal files found. Possible cabal files: \""+ ++ intercalate "\", \"" cfs ++"\"."++ghcExceptionDoc :: GhcException -> Doc+ghcExceptionDoc e@(CmdLineError _) =+ text $ "<command line>: " ++ showGhcException e ""+ghcExceptionDoc (UsageError str) = strDoc str+ghcExceptionDoc (Panic msg) = vcat $ map text $ lines $ printf "\+\GHC panic! (the 'impossible' happened)\n\+\ ghc-mod version %s\n\+\ GHC library version %s for %s:\n\+\ %s\n\+\\n\+\Please report this as a bug: %s\n"+ gmVer ghcVer platform msg url+ where+ gmVer = showVersion version+ ghcVer = cProjectVersion+ platform = cHostPlatformString+ url = "https://github.com/kazu-yamamoto/ghc-mod/issues" :: String++ghcExceptionDoc e = text $ showGhcException e ""++liftMaybe :: MonadError e m => e -> m (Maybe a) -> m a+liftMaybe e action = maybe (throwError e) return =<< action++overrideError :: MonadError e m => e -> m a -> m a+overrideError e action = modifyError (const e) action++modifyError :: MonadError e m => (e -> e) -> m a -> m a+modifyError f action = action `catchError` \e -> throwError $ f e++infixr 0 `modifyError'`+modifyError' :: MonadError e m => m a -> (e -> e) -> m a+modifyError' = flip modifyError++modifyGmError :: (MonadIO m, ExceptionMonad m)+ => (GhcModError -> GhcModError) -> m a -> m a+modifyGmError f a = gcatch a $ \(ex :: GhcModError) -> liftIO $ throwIO (f ex)++tryFix :: MonadError e m => m a -> (e -> m ()) -> m a+tryFix action f = do+ action `catchError` \e -> f e >> action++data GHandler m a = forall e . Exception e => GHandler (e -> m a)++gcatches :: (MonadIO m, ExceptionMonad m) => m a -> [GHandler m a] -> m a+gcatches io handlers = io `gcatch` gcatchesHandler handlers++gcatchesHandler :: (MonadIO m, ExceptionMonad m)+ => [GHandler m a] -> SomeException -> m a+gcatchesHandler handlers e = foldr tryHandler (liftIO $ throw e) handlers+ where tryHandler (GHandler handler) res+ = case fromException e of+ Just e' -> handler e'+ Nothing -> res
+ core/GhcMod/FileMapping.hs view
@@ -0,0 +1,104 @@+module GhcMod.FileMapping+ ( loadMappedFile+ , loadMappedFileSource+ , unloadMappedFile+ , mapFile+ , fileModSummaryWithMapping+ ) where++import GhcMod.Types+import GhcMod.Monad.Types+import GhcMod.Gap+import GhcMod.HomeModuleGraph+import GhcMod.Utils++import System.IO+import System.FilePath+import System.Directory++import Control.Monad.Trans.Maybe+import GHC+import Control.Monad++{- | maps 'FilePath', given as first argument to take source from+'FilePath' given as second argument. Works exactly the same as+first form of `--map-file` CLI option.++\'from\' can be either full path, or path relative to project root.+\'to\' has to be either relative to project root, or full path (preferred)+-}+loadMappedFile :: IOish m+ => FilePath -- ^ \'from\', file that will be mapped+ -> FilePath -- ^ \'to\', file to take source from+ -> GhcModT m ()+loadMappedFile from to = loadMappedFile' from to False++{- |+maps 'FilePath', given as first argument to have source as given+by second argument.++\'from\' may or may not exist, and should be either full path,+or relative to project root.+-}+loadMappedFileSource :: IOish m+ => FilePath -- ^ \'from\', file that will be mapped+ -> String -- ^ \'src\', source+ -> GhcModT m ()+loadMappedFileSource from src = do+ tmpdir <- cradleTempDir `fmap` cradle+ enc <- liftIO . mkTextEncoding . optEncoding =<< options+ to <- liftIO $ do+ (fn, h) <- openTempFile tmpdir (takeFileName from)+ hSetEncoding h enc+ hPutStr h src+ hClose h+ return fn+ loadMappedFile' from to True++loadMappedFile' :: IOish m => FilePath -> FilePath -> Bool -> GhcModT m ()+loadMappedFile' from to isTemp = do+ cfn <- getCanonicalFileNameSafe from+ unloadMappedFile' cfn+ crdl <- cradle+ let to' = makeRelative (cradleRootDir crdl) to+ addMMappedFile cfn (FileMapping to' isTemp)++mapFile :: (IOish m, GmState m) => HscEnv -> Target -> m Target+mapFile _ (Target tid@(TargetFile filePath _) taoc _) = do+ mapping <- lookupMMappedFile filePath+ return $ mkMappedTarget (Just filePath) tid taoc mapping+mapFile env (Target tid@(TargetModule moduleName) taoc _) = do+ (fp, mapping) <- do+ filePath <- fmap (fmap mpPath) (liftIO $ findModulePath env moduleName)+ mmf <- runMaybeT $ MaybeT (return filePath) >>= MaybeT . lookupMMappedFile+ return (filePath, mmf)+ return $ mkMappedTarget fp tid taoc mapping++mkMappedTarget :: Maybe FilePath -> TargetId -> Bool -> Maybe FileMapping -> Target+mkMappedTarget _ _ taoc (Just to) =+ mkTarget (TargetFile (fmPath to) Nothing) taoc Nothing+mkMappedTarget _ tid taoc _ =+ mkTarget tid taoc Nothing++{-|+unloads previously mapped file \'file\', so that it's no longer mapped,+and removes any temporary files created when file was+mapped.++\'file\' should be either full path, or relative to project root.+-}+unloadMappedFile :: IOish m+ => FilePath -- ^ \'file\', file to unmap+ -> GhcModT m ()+unloadMappedFile = getCanonicalFileNameSafe >=> unloadMappedFile'++unloadMappedFile' :: IOish m => FilePath -> GhcModT m ()+unloadMappedFile' cfn = void $ runMaybeT $ do+ fm <- MaybeT $ lookupMMappedFile cfn+ liftIO $ when (fmTemp fm) $ removeFile (fmPath fm)+ delMMappedFile cfn++fileModSummaryWithMapping :: (IOish m, GmState m, GhcMonad m, GmEnv m) =>+ FilePath -> m ModSummary+fileModSummaryWithMapping fn =+ withMappedFile fn $ \fn' -> fileModSummary fn'
+ core/GhcMod/Gap.hs view
@@ -0,0 +1,723 @@+{-# LANGUAGE TypeSynonymInstances, FlexibleInstances, CPP, ScopedTypeVariables, RankNTypes #-}++module GhcMod.Gap (+ GhcMod.Gap.ClsInst+ , mkTarget+ , withStyle+ , GmLogAction+ , setLogAction+ , getSrcSpan+ , getSrcFile+ , withInteractiveContext+ , ghcCmdOptions+ , toStringBuffer+ , showSeverityCaption+ , setCabalPkg+ , setHideAllPackages+ , setDeferTypeErrors+ , setDeferTypedHoles+ , setWarnTypedHoles+ , setDumpSplices+ , setNoMaxRelevantBindings+ , isDumpSplices+ , filterOutChildren+ , infoThing+ , pprInfo+ , HasType(..)+ , errorMsgSpan+ , setErrorMsgSpan+ , typeForUser+ , nameForUser+ , occNameForUser+ , deSugar+ , showDocWith+ , renderGm+ , GapThing(..)+ , fromTyThing+ , fileModSummary+ , WarnFlags+ , emptyWarnFlags+ , GLMatch+ , GLMatchI+ , getClass+ , occName+ , listVisibleModuleNames+ , listVisibleModules+ , lookupModulePackageInAllPackages+ , GhcMod.Gap.isSynTyCon+ , parseModuleHeader+ , mkErrStyle'+ , everythingStagedWithContext+ , withCleanupSession+ ) where++import Control.Applicative hiding (empty)+import Control.Monad (filterM)+import CoreSyn (CoreExpr)+import Data.List (intersperse)+import Data.Maybe (catMaybes)+import Data.Time.Clock (UTCTime)+import Data.Traversable hiding (mapM)+import DataCon (dataConUserType)+import Desugar (deSugarExpr)+import DynFlags+import ErrUtils+import Exception+import FastString+import GhcMonad+import HscTypes+import NameSet+import OccName+import Outputable+import PprTyThing+import StringBuffer+import TcType+import Var (varType)+import System.Directory+import SysTools+#if __GLASGOW_HASKELL__ >= 800+import GHCi (stopIServ)+#endif++import qualified Name+import qualified InstEnv+import qualified Pretty+import qualified StringBuffer as SB++#if __GLASGOW_HASKELL__ >= 710+import CoAxiom (coAxiomTyCon)+#endif++#if __GLASGOW_HASKELL__ >= 708+import FamInstEnv+import ConLike (ConLike(..))+import PatSyn+#else+import TcRnTypes+#endif++-- GHC 7.8 doesn't define this macro, nor does GHC 7.10.0+-- It IS defined from 7.10.1 and up though.+-- So we can only test for 7.10.1.0 and up with it.+#if __GLASGOW_HASKELL__ < 710+#ifndef MIN_VERSION_GLASGOW_HASKELL+#define MIN_VERSION_GLASGOW_HASKELL(a,b,c,d) FALSE+#endif+#endif++#if MIN_VERSION_GLASGOW_HASKELL(8,0,1,20161117)+import GHC hiding (ClsInst, withCleanupSession)+import qualified GHC (withCleanupSession)+#elif __GLASGOW_HASKELL__ >= 706+import GHC hiding (ClsInst)+#else+import GHC hiding (Instance)+import Control.Arrow hiding ((<+>))+import Data.Convertible+import RdrName (rdrNameOcc)+#endif++#if __GLASGOW_HASKELL__ < 710+import UniqFM (eltsUFM)+import Module+#endif++#if __GLASGOW_HASKELL__ >= 704+import qualified Data.IntSet as I (IntSet, empty)+#endif++#if __GLASGOW_HASKELL__ < 706+import Control.DeepSeq (NFData(rnf))+import Data.ByteString.Lazy.Internal (ByteString(..))+#endif++import Bag+import Lexer as L+import Parser+import SrcLoc+import Packages+import Data.Generics (GenericQ, extQ, gmapQ)+import GHC.SYB.Utils (Stage(..))++import GhcMod.Types (Expression(..))+import Prelude++----------------------------------------------------------------+----------------------------------------------------------------+--+#if __GLASGOW_HASKELL__ >= 706+type ClsInst = InstEnv.ClsInst+#else+type ClsInst = InstEnv.Instance+#endif++mkTarget :: TargetId -> Bool -> Maybe (SB.StringBuffer, UTCTime) -> Target+#if __GLASGOW_HASKELL__ >= 706+mkTarget = Target+#else+mkTarget tid allowObjCode = Target tid allowObjCode . (fmap . second) convert+#endif++----------------------------------------------------------------+----------------------------------------------------------------++withStyle :: DynFlags -> PprStyle -> SDoc -> Pretty.Doc+#if __GLASGOW_HASKELL__ >= 706+withStyle = withPprStyleDoc+#else+withStyle _ = withPprStyleDoc+#endif++#if __GLASGOW_HASKELL__ >= 800+-- flip LogAction+type GmLogAction = WarnReason -> DynFlags -> Severity -> SrcSpan -> PprStyle -> MsgDoc -> IO ()+#elif __GLASGOW_HASKELL__ >= 706+type GmLogAction = forall a. a -> LogAction+#else+type GmLogAction = forall a. a -> DynFlags -> LogAction+#endif++-- DynFlags -> WarnReason -> Severity -> SrcSpan -> PprStyle -> MsgDoc -> IO ()++setLogAction :: DynFlags -> GmLogAction -> DynFlags+setLogAction df f =+#if __GLASGOW_HASKELL__ >= 800+ df { log_action = flip f }+#elif __GLASGOW_HASKELL__ >= 706+ df { log_action = f (error "setLogAction") }+#else+ df { log_action = f (error "setLogAction") df }+#endif++showDocWith :: DynFlags -> Pretty.Mode -> Pretty.Doc -> String+#if __GLASGOW_HASKELL__ >= 800+showDocWith dflags mode = Pretty.renderStyle mstyle where+ mstyle = Pretty.style { Pretty.mode = mode, Pretty.lineLength = pprCols dflags }+#elif __GLASGOW_HASKELL__ >= 708+-- Pretty.showDocWith disappeard.+-- https://github.com/ghc/ghc/commit/08a3536e4246e323fbcd8040e0b80001950fe9bc+showDocWith dflags mode = Pretty.showDoc mode (pprCols dflags)+#else+showDocWith _ = Pretty.showDocWith+#endif++renderGm :: Pretty.Doc -> String+#if __GLASGOW_HASKELL__ >= 800+renderGm = Pretty.fullRender Pretty.PageMode 80 1.2 string_txt ""+#else+renderGm = Pretty.fullRender Pretty.PageMode 80 1.2 string_txt ""+#endif+ where+ string_txt :: Pretty.TextDetails -> String -> String+ string_txt (Pretty.Chr c) s = c:s+ string_txt (Pretty.Str s1) s2 = s1 ++ s2+ string_txt (Pretty.PStr s1) s2 = unpackFS s1 ++ s2+ string_txt (Pretty.LStr s1 _) s2 = unpackLitString s1 ++ s2+#if __GLASGOW_HASKELL__ >= 708+ string_txt (Pretty.ZStr s1) s2 = zString s1 ++ s2+#endif+++----------------------------------------------------------------+----------------------------------------------------------------++getSrcSpan :: SrcSpan -> Maybe (Int,Int,Int,Int)+#if __GLASGOW_HASKELL__ >= 702+getSrcSpan (RealSrcSpan spn)+#else+getSrcSpan spn | isGoodSrcSpan spn+#endif+ = Just (srcSpanStartLine spn+ , srcSpanStartCol spn+ , srcSpanEndLine spn+ , srcSpanEndCol spn)+getSrcSpan _ = Nothing++getSrcFile :: SrcSpan -> Maybe String+#if __GLASGOW_HASKELL__ >= 702+getSrcFile (RealSrcSpan spn) = Just . unpackFS . srcSpanFile $ spn+#else+getSrcFile spn | isGoodSrcSpan spn = Just . unpackFS . srcSpanFile $ spn+#endif+getSrcFile _ = Nothing++----------------------------------------------------------------++toStringBuffer :: GhcMonad m => [String] -> m StringBuffer+#if __GLASGOW_HASKELL__ >= 702+toStringBuffer = return . stringToStringBuffer . unlines+#else+toStringBuffer = liftIO . stringToStringBuffer . unlines+#endif++----------------------------------------------------------------++ghcCmdOptions :: [String]+#if __GLASGOW_HASKELL__ >= 710+-- this also includes -X options and all sorts of other things so the+ghcCmdOptions = flagsForCompletion False+#else+ghcCmdOptions = [ "-f" ++ prefix ++ option+ | option <- opts+ , prefix <- ["","no-"]+ ]+# if __GLASGOW_HASKELL__ >= 704+ where opts =+ [option | (option,_,_) <- fFlags]+ ++ [option | (option,_,_) <- fWarningFlags]+ ++ [option | (option,_,_) <- fLangFlags]+# else+ where opts =+ [option | (option,_,_,_) <- fFlags]+ ++ [option | (option,_,_,_) <- fWarningFlags]+ ++ [option | (option,_,_,_) <- fLangFlags]+# endif+#endif++----------------------------------------------------------------+----------------------------------------------------------------++fileModSummary :: GhcMonad m => FilePath -> m ModSummary+fileModSummary file' = do+ mss <- getModuleGraph+ file <- liftIO $ canonicalizePath file'+ [ms] <- liftIO $ flip filterM mss $ \m ->+ (Just file==) <$> canonicalizePath `traverse` ml_hs_file (ms_location m)+ return ms++withInteractiveContext :: GhcMonad m => m a -> m a+withInteractiveContext action = gbracket setup teardown body+ where+ setup = getContext+ teardown = setCtx+ body _ = do+ topImports >>= setCtx+ action+ topImports = do+ ms <- filterM moduleIsInterpreted =<< map ms_mod <$> getModuleGraph+ let iis = map (IIModule . modName) ms+#if __GLASGOW_HASKELL__ >= 704+ return iis+#else+ return (iis,[])+#endif+#if __GLASGOW_HASKELL__ >= 706+ modName = moduleName+ setCtx = setContext+#elif __GLASGOW_HASKELL__ >= 704+ modName = id+ setCtx = setContext+#else+ modName = ms_mod+ setCtx = uncurry setContext+#endif++showSeverityCaption :: Severity -> String+#if __GLASGOW_HASKELL__ >= 706+showSeverityCaption SevWarning = "Warning: "+showSeverityCaption _ = ""+#else+showSeverityCaption = const ""+#endif++----------------------------------------------------------------+----------------------------------------------------------------++setCabalPkg :: DynFlags -> DynFlags+#if __GLASGOW_HASKELL__ >= 708+setCabalPkg dflag = gopt_set dflag Opt_BuildingCabalPackage+#else+setCabalPkg dflag = dopt_set dflag Opt_BuildingCabalPackage+#endif++----------------------------------------------------------------++setHideAllPackages :: DynFlags -> DynFlags+#if __GLASGOW_HASKELL__ >= 708+setHideAllPackages df = gopt_set df Opt_HideAllPackages+#else+setHideAllPackages df = dopt_set df Opt_HideAllPackages+#endif++----------------------------------------------------------------++setDumpSplices :: DynFlags -> DynFlags+setDumpSplices dflag = dopt_set dflag Opt_D_dump_splices++isDumpSplices :: DynFlags -> Bool+isDumpSplices dflag = dopt Opt_D_dump_splices dflag++----------------------------------------------------------------+++setDeferTypeErrors :: DynFlags -> DynFlags+#if __GLASGOW_HASKELL__ >= 708+setDeferTypeErrors dflag = gopt_set dflag Opt_DeferTypeErrors+#elif __GLASGOW_HASKELL__ >= 706+setDeferTypeErrors dflag = dopt_set dflag Opt_DeferTypeErrors+#else+setDeferTypeErrors = id+#endif++setDeferTypedHoles :: DynFlags -> DynFlags+#if __GLASGOW_HASKELL__ >= 710+setDeferTypedHoles dflag = gopt_set dflag Opt_DeferTypedHoles+#else+setDeferTypedHoles = id+#endif++setWarnTypedHoles :: DynFlags -> DynFlags+#if __GLASGOW_HASKELL__ >= 708+setWarnTypedHoles dflag = wopt_set dflag Opt_WarnTypedHoles+#else+setWarnTypedHoles = id+#endif++----------------------------------------------------------------++-- | Set 'DynFlags' equivalent to "-fno-max-relevant-bindings".+setNoMaxRelevantBindings :: DynFlags -> DynFlags+#if __GLASGOW_HASKELL__ >= 708+setNoMaxRelevantBindings df = df { maxRelevantBinds = Nothing }+#else+setNoMaxRelevantBindings = id+#endif++----------------------------------------------------------------+----------------------------------------------------------------++class HasType a where+ getType :: GhcMonad m => TypecheckedModule -> a -> m (Maybe (SrcSpan, Type))+++instance HasType (LHsBind Id) where+#if __GLASGOW_HASKELL__ >= 708+ getType _ (L spn FunBind{fun_matches = m}) = return $ Just (spn, typ)+ where in_tys = mg_arg_tys m+ out_typ = mg_res_ty m+ typ = mkFunTys in_tys out_typ+#else+ getType _ (L spn FunBind{fun_matches = MatchGroup _ typ}) = return $ Just (spn, typ)+#endif+ getType _ _ = return Nothing++----------------------------------------------------------------+----------------------------------------------------------------+-- from ghc/InteractiveUI.hs++filterOutChildren :: (a -> TyThing) -> [a] -> [a]+filterOutChildren get_thing xs+ = [x | x <- xs, not (getName (get_thing x) `elemNameSet` implicits)]+ where+ implicits = mkNameSet [getName t | x <- xs, t <- implicitTyThings (get_thing x)]++infoThing :: GhcMonad m => (FilePath -> FilePath) -> Expression -> m SDoc+infoThing m (Expression str) = do+ names <- parseName str+#if __GLASGOW_HASKELL__ >= 708+ mb_stuffs <- mapM (getInfo False) names+ let filtered = filterOutChildren (\(t,_f,_i,_fam) -> t) (catMaybes mb_stuffs)+#else+ mb_stuffs <- mapM getInfo names+ let filtered = filterOutChildren (\(t,_f,_i) -> t) (catMaybes mb_stuffs)+#endif+ return $ vcat (intersperse (text "") $ map (pprInfo m False) filtered)++#if __GLASGOW_HASKELL__ >= 708+pprInfo :: (FilePath -> FilePath) -> Bool -> (TyThing, GHC.Fixity, [ClsInst], [FamInst]) -> SDoc+pprInfo m _ (thing, fixity, insts, famInsts)+ = pprTyThingInContextLoc' thing+ $$ show_fixity fixity+ $$ vcat (map pprInstance' insts)+ $$ vcat (map pprFamInst' famInsts)+#else+pprInfo :: (FilePath -> FilePath) -> PrintExplicitForalls -> (TyThing, GHC.Fixity, [ClsInst]) -> SDoc+pprInfo m pefas (thing, fixity, insts)+ = pprTyThingInContextLoc' pefas thing+ $$ show_fixity fixity+ $$ vcat (map pprInstance' insts)+#endif+ where+ show_fixity fx+ | fx == defaultFixity = Outputable.empty+ | otherwise = ppr fx <+> ppr (getName thing)+#if __GLASGOW_HASKELL__ >= 708+ pprTyThingInContextLoc' thing' = showWithLoc (pprDefinedAt' thing') (pprTyThingInContext thing')+#if __GLASGOW_HASKELL__ >= 710+ pprFamInst' (FamInst { fi_flavor = DataFamilyInst rep_tc })+ = pprTyThingInContextLoc (ATyCon rep_tc)++ pprFamInst' (FamInst { fi_flavor = SynFamilyInst, fi_axiom = axiom+ , fi_tys = lhs_tys, fi_rhs = rhs })+ = showWithLoc (pprDefinedAt' (getName axiom)) $+ hang (ptext (sLit "type instance") <+> pprTypeApp (coAxiomTyCon axiom) lhs_tys)+ 2 (equals <+> ppr rhs)+#else+ pprFamInst' ispec = showWithLoc (pprDefinedAt' (getName ispec)) (pprFamInstHdr ispec)+#endif+#else+ pprTyThingInContextLoc' pefas' thing' = showWithLoc (pprDefinedAt' thing') (pprTyThingInContext pefas' thing')+#endif+ showWithLoc loc doc+ = hang doc 2 (char '\t' <> comment <+> loc)+ -- The tab tries to make them line up a bit+ where+ comment = ptext (sLit "--")+ pprInstance' ispec = hang (pprInstanceHdr ispec)+ 2 (ptext (sLit "--") <+> pprDefinedAt' (getName ispec))+ pprDefinedAt' thing' = ptext (sLit "Defined") <+> pprNameDefnLoc' (getName thing')+ pprNameDefnLoc' name+ = case Name.nameSrcLoc name of+ RealSrcLoc s -> ptext (sLit "at") <+> ppr (subst s)+ UnhelpfulLoc s+ | Name.isInternalName name || Name.isSystemName name+ -> ptext (sLit "at") <+> ftext s+ | otherwise+ -> ptext (sLit "in") <+> quotes (ppr (nameModule name))+ where subst s = mkRealSrcLoc (realFP s) (srcLocLine s) (srcLocCol s)+ realFP = mkFastString . m . unpackFS . srcLocFile++----------------------------------------------------------------+----------------------------------------------------------------++errorMsgSpan :: ErrMsg -> SrcSpan+#if __GLASGOW_HASKELL__ >= 708+errorMsgSpan = errMsgSpan+#else+errorMsgSpan = head . errMsgSpans+#endif++setErrorMsgSpan :: ErrMsg -> SrcSpan -> ErrMsg+#if __GLASGOW_HASKELL__ >= 708+setErrorMsgSpan err s = err { errMsgSpan = s }+#else+setErrorMsgSpan err s = err { errMsgSpans = [s] }+#endif++typeForUser :: Type -> SDoc+#if __GLASGOW_HASKELL__ >= 708+typeForUser = pprTypeForUser+#else+typeForUser = pprTypeForUser False+#endif++nameForUser :: Name -> SDoc+nameForUser = pprOccName . getOccName++occNameForUser :: OccName -> SDoc+occNameForUser = pprOccName++deSugar :: TypecheckedModule -> LHsExpr Id -> HscEnv+ -> IO (Maybe CoreExpr)+#if __GLASGOW_HASKELL__ >= 708+deSugar _ e hs_env = snd <$> deSugarExpr hs_env e+#else+deSugar tcm e hs_env = snd <$> deSugarExpr hs_env modu rn_env ty_env e+ where+ modu = ms_mod $ pm_mod_summary $ tm_parsed_module tcm+ tcgEnv = fst $ tm_internals_ tcm+ rn_env = tcg_rdr_env tcgEnv+ ty_env = tcg_type_env tcgEnv+#endif++----------------------------------------------------------------+----------------------------------------------------------------++data GapThing = GtA Type+ | GtT TyCon+ | GtN+#if __GLASGOW_HASKELL__ >= 800+ | GtPatSyn PatSyn+#endif++fromTyThing :: TyThing -> GapThing+fromTyThing (AnId i) = GtA $ varType i+#if __GLASGOW_HASKELL__ >= 708+fromTyThing (AConLike (RealDataCon d)) = GtA $ dataConUserType d+#if __GLASGOW_HASKELL__ >= 800+fromTyThing (AConLike (PatSynCon p)) = GtPatSyn p+#else+fromTyThing (AConLike (PatSynCon p)) = GtA $ patSynType p+#endif+#else+fromTyThing (ADataCon d) = GtA $ dataConUserType d+#endif+fromTyThing (ATyCon t) = GtT t+fromTyThing _ = GtN++----------------------------------------------------------------+----------------------------------------------------------------++#if __GLASGOW_HASKELL__ >= 704+type WarnFlags = I.IntSet+emptyWarnFlags :: WarnFlags+emptyWarnFlags = I.empty+#else+type WarnFlags = [WarningFlag]+emptyWarnFlags :: WarnFlags+emptyWarnFlags = []+#endif++----------------------------------------------------------------+----------------------------------------------------------------++#if __GLASGOW_HASKELL__ >= 708+type GLMatch = LMatch RdrName (LHsExpr RdrName)+type GLMatchI = LMatch Id (LHsExpr Id)+#else+type GLMatch = LMatch RdrName+type GLMatchI = LMatch Id+#endif++getClass :: [LInstDecl Name] -> Maybe (Name, SrcSpan)+#if __GLASGOW_HASKELL__ >= 800+-- Instance declarations of sort 'instance F (G a)'+getClass [L loc (ClsInstD (ClsInstDecl {cid_poly_ty = HsIB _ (L _ (HsForAllTy _ (L _ (HsAppTy (L _ (HsTyVar (L _ className))) _))))}))] = Just (className, loc)+-- Instance declarations of sort 'instance F G' (no variables)+getClass [L loc (ClsInstD (ClsInstDecl {cid_poly_ty = HsIB _ (L _ (HsAppTy (L _ (HsTyVar (L _ className))) _))}))] = Just (className, loc)+#elif __GLASGOW_HASKELL__ >= 710+-- Instance declarations of sort 'instance F (G a)'+getClass [L loc (ClsInstD (ClsInstDecl {cid_poly_ty = (L _ (HsForAllTy _ _ _ _ (L _ (HsAppTy (L _ (HsTyVar className)) _))))}))] = Just (className, loc)+-- Instance declarations of sort 'instance F G' (no variables)+getClass [L loc (ClsInstD (ClsInstDecl {cid_poly_ty = (L _ (HsAppTy (L _ (HsTyVar className)) _))}))] = Just (className, loc)+#elif __GLASGOW_HASKELL__ >= 708+-- Instance declarations of sort 'instance F (G a)'+getClass [L loc (ClsInstD (ClsInstDecl {cid_poly_ty = (L _ (HsForAllTy _ _ _ (L _ (HsAppTy (L _ (HsTyVar className)) _))))}))] = Just (className, loc)+-- Instance declarations of sort 'instance F G' (no variables)+getClass [L loc (ClsInstD (ClsInstDecl {cid_poly_ty = (L _ (HsAppTy (L _ (HsTyVar className)) _))}))] = Just (className, loc)+#elif __GLASGOW_HASKELL__ >= 706+getClass [L loc (ClsInstD (L _ (HsForAllTy _ _ _ (L _ (HsAppTy (L _ (HsTyVar className)) _)))) _ _ _)] = Just (className, loc)+getClass[L loc (ClsInstD (L _ (HsAppTy (L _ (HsTyVar className)) _)) _ _ _)] = Just (className, loc)+#else+getClass [L loc (InstDecl (L _ (HsForAllTy _ _ _ (L _ (HsAppTy (L _ (HsTyVar className)) _)))) _ _ _)] = Just (className, loc)+getClass [L loc (InstDecl (L _ (HsAppTy (L _ (HsTyVar className)) _)) _ _ _)] = Just (className, loc)+#endif+getClass _ = Nothing++#if __GLASGOW_HASKELL__ < 706+occName :: RdrName -> OccName+occName = rdrNameOcc+#endif++----------------------------------------------------------------++#if __GLASGOW_HASKELL__ < 710+-- Copied from ghc/InteractiveUI.hs+allExposedPackageConfigs :: DynFlags -> [PackageConfig]+allExposedPackageConfigs df = filter exposed $ eltsUFM $ pkgIdMap $ pkgState df++allExposedModules :: DynFlags -> [ModuleName]+allExposedModules df = concat $ map exposedModules $ allExposedPackageConfigs df++listVisibleModuleNames :: DynFlags -> [ModuleName]+listVisibleModuleNames = allExposedModules+#endif++lookupModulePackageInAllPackages ::+ DynFlags -> ModuleName -> [String]+lookupModulePackageInAllPackages df mn =+#if __GLASGOW_HASKELL__ >= 710+ unpackSPId . sourcePackageId . snd <$> lookupModuleInAllPackages df mn+ where+ unpackSPId (SourcePackageId fs) = unpackFS fs+#else+ unpackPId . sourcePackageId . fst <$> lookupModuleInAllPackages df mn+ where+ unpackPId pid = packageIdString $ mkPackageId pid+-- n ++ "-" ++ showVersion v+#endif++listVisibleModules :: DynFlags -> [GHC.Module]+listVisibleModules df = let+#if __GLASGOW_HASKELL__ >= 710+ modNames = listVisibleModuleNames df+ mods = [ m | mn <- modNames, (m, _) <- lookupModuleInAllPackages df mn ]+#else+ pkgCfgs = allExposedPackageConfigs df+ mods = [ mkModule pid modname | p <- pkgCfgs+ , let pid = packageConfigId p+ , modname <- exposedModules p ]+#endif+ in mods++isSynTyCon :: TyCon -> Bool+#if __GLASGOW_HASKELL__ >= 710+isSynTyCon = GHC.isTypeSynonymTyCon+#else+isSynTyCon = GHC.isSynTyCon+#endif+++parseModuleHeader+ :: String -- ^ Haskell module source text (full Unicode is supported)+ -> DynFlags+ -> FilePath -- ^ the filename (for source locations)+ -> Either ErrorMessages (WarningMessages, Located (HsModule RdrName))+parseModuleHeader str dflags filename =+ let+ loc = mkRealSrcLoc (mkFastString filename) 1 1+ buf = stringToStringBuffer str+ in+ case L.unP Parser.parseHeader (mkPState dflags buf loc) of++ PFailed sp err ->+#if __GLASGOW_HASKELL__ >= 706+ Left (unitBag (mkPlainErrMsg dflags sp err))+#else+ Left (unitBag (mkPlainErrMsg sp err))+#endif++ POk pst rdr_module ->+ let (warns,_) = getMessages pst in+ Right (warns, rdr_module)++mkErrStyle' :: DynFlags -> PrintUnqualified -> PprStyle+#if __GLASGOW_HASKELL__ >= 706+mkErrStyle' = Outputable.mkErrStyle+#else+mkErrStyle' _ = Outputable.mkErrStyle+#endif++#if __GLASGOW_HASKELL__ < 706+instance NFData ByteString where+ rnf Empty = ()+ rnf (Chunk _ b) = rnf b+#endif++-- | Like 'everything', but avoid known potholes, based on the 'Stage' that+-- generated the Ast.+everythingStagedWithContext :: Stage -> s -> (r -> r -> r) -> r -> GenericQ (s -> (r, s)) -> GenericQ r+everythingStagedWithContext stage s0 f z q x+ | (const False+#if __GLASGOW_HASKELL__ <= 708+ `extQ` postTcType+#endif+ `extQ` fixity `extQ` nameSet) x = z+ | otherwise = foldl f r (gmapQ (everythingStagedWithContext stage s' f z q) x)+ where nameSet = const (stage `elem` [Parser,TypeChecker]) :: NameSet -> Bool+#if __GLASGOW_HASKELL__ <= 708+ postTcType = const (stage<TypeChecker) :: PostTcType -> Bool+#endif+ fixity = const (stage<Renamer) :: GHC.Fixity -> Bool+ (r, s') = q x s0++withCleanupSession :: GhcMonad m => m a -> m a+#if __GLASGOW_HASKELL__ >= 800+#if MIN_VERSION_GLASGOW_HASKELL(8,0,1,20161117)+withCleanupSession = GHC.withCleanupSession+#else+withCleanupSession ghc = ghc `gfinally` cleanup+ where+ cleanup = do+ hsc_env <- getSession+ let dflags = hsc_dflags hsc_env+ liftIO $ do+ cleanTempFiles dflags+ cleanTempDirs dflags+ stopIServ hsc_env+#endif+#else+withCleanupSession action = do+ df <- getSessionDynFlags+ GHC.defaultCleanupHandler df action+#endif
+ core/GhcMod/GhcPkg.hs view
@@ -0,0 +1,109 @@+{-# LANGUAGE BangPatterns, ScopedTypeVariables, TupleSections #-}+module GhcMod.GhcPkg (+ ghcPkgDbOpt+ , ghcPkgDbStackOpts+ , ghcDbStackOpts+ , ghcDbOpt+ , getPackageDbStack+ , getPackageCachePaths+ , getGhcPkgProgram+ ) where++import Config (cProjectVersion, cTargetPlatformString, cProjectVersionInt)+import Control.Applicative+import Data.List.Split (splitOn)+import Data.Maybe+import Exception (handleIO)+import System.Directory (doesDirectoryExist, getAppUserDataDirectory)+import System.FilePath ((</>))+import Prelude++import GhcMod.Types+import GhcMod.Monad.Types+import GhcMod.CabalHelper+import GhcMod.PathsAndFiles+import GhcMod.CustomPackageDb+import GhcMod.Stack++ghcVersion :: Int+ghcVersion = read cProjectVersionInt++----------------------------------------------------------------++-- | Get options needed to add a list of package dbs to ghc-pkg's db stack+ghcPkgDbStackOpts :: [GhcPkgDb] -- ^ Package db stack+ -> [String]+ghcPkgDbStackOpts dbs = ghcPkgDbOpt `concatMap` dbs++-- | Get options needed to add a list of package dbs to ghc's db stack+ghcDbStackOpts :: [GhcPkgDb] -- ^ Package db stack+ -> [String]+ghcDbStackOpts dbs = ghcDbOpt `concatMap` dbs++----------------------------------------------------------------++ghcPkgDbOpt :: GhcPkgDb -> [String]+ghcPkgDbOpt GlobalDb = ["--global"]+ghcPkgDbOpt UserDb = ["--user"]+ghcPkgDbOpt (PackageDb pkgDb)+ | ghcVersion < 706 = ["--no-user-package-conf", "--package-conf=" ++ pkgDb]+ | otherwise = ["--no-user-package-db", "--package-db=" ++ pkgDb]++ghcDbOpt :: GhcPkgDb -> [String]+ghcDbOpt GlobalDb+ | ghcVersion < 706 = ["-global-package-conf"]+ | otherwise = ["-global-package-db"]+ghcDbOpt UserDb+ | ghcVersion < 706 = ["-user-package-conf"]+ | otherwise = ["-user-package-db"]+ghcDbOpt (PackageDb pkgDb)+ | ghcVersion < 706 = ["-no-user-package-conf", "-package-conf", pkgDb]+ | otherwise = ["-no-user-package-db", "-package-db", pkgDb]++----------------------------------------------------------------++getGhcPkgProgram :: IOish m => GhcModT m FilePath+getGhcPkgProgram = do+ crdl <- cradle+ progs <- optPrograms <$> options+ case cradleProject crdl of+ (StackProject senv) -> do+ Just ghcPkg <- getStackGhcPkgPath senv+ return ghcPkg+ _ ->+ return $ ghcPkgProgram progs++getPackageDbStack :: IOish m => GhcModT m [GhcPkgDb]+getPackageDbStack = do+ crdl <- cradle+ mCusPkgStack <- getCustomPkgDbStack+ stack <- case cradleProject crdl of+ PlainProject ->+ return [GlobalDb, UserDb]+ SandboxProject -> do+ Just db <- liftIO $ getSandboxDb crdl+ return $ [GlobalDb, db]+ CabalProject ->+ getCabalPackageDbStack+ (StackProject StackEnv {..}) ->+ return $ [GlobalDb, PackageDb seSnapshotPkgDb, PackageDb seLocalPkgDb]+ return $ fromMaybe stack mCusPkgStack++getPackageCachePaths :: IOish m => FilePath -> GhcModT m [FilePath]+getPackageCachePaths sysPkgCfg = do+ pkgDbStack <- getPackageDbStack+ catMaybes <$> (liftIO . resolvePackageConfig sysPkgCfg) `mapM` pkgDbStack++-- TODO: use PkgConfRef+--- Copied from ghc module `Packages' unfortunately it's not exported :/+resolvePackageConfig :: FilePath -> GhcPkgDb -> IO (Maybe FilePath)+resolvePackageConfig sysPkgCfg GlobalDb = return $ Just sysPkgCfg+resolvePackageConfig _ UserDb = handleIO (\_ -> return Nothing) $ do+ appdir <- getAppUserDataDirectory "ghc"+ let dir = appdir </> (target_arch ++ '-':target_os ++ '-':cProjectVersion)+ pkgconf = dir </> "package.conf.d"+ exist <- doesDirectoryExist pkgconf+ return $ if exist then Just pkgconf else Nothing+ where+ [target_arch,_,target_os] = splitOn "-" cTargetPlatformString+resolvePackageConfig _ (PackageDb name) = return $ Just name
+ core/GhcMod/HomeModuleGraph.hs view
@@ -0,0 +1,271 @@+-- ghc-mod: Happy Haskell Hacking+-- Copyright (C) 2015 Daniel Gröber <dxld ÄT darkboxed DOT org>+--+-- This program is free software: you can redistribute it and/or modify+-- it under the terms of the GNU Affero General Public License as published by+-- the Free Software Foundation, either version 3 of the License, or+-- (at your option) any later version.+--+-- This program is distributed in the hope that it will be useful,+-- but WITHOUT ANY WARRANTY; without even the implied warranty of+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+-- GNU Affero General Public License for more details.+--+-- You should have received a copy of the GNU Affero General Public License+-- along with this program. If not, see <http://www.gnu.org/licenses/>.++{-# LANGUAGE ScopedTypeVariables, RecordWildCards #-}+module GhcMod.HomeModuleGraph (+ GmModuleGraph(..)+ , ModulePath(..)+ , mkFileMap+ , mkModuleMap+ , mkMainModulePath+ , findModulePath+ , findModulePathSet+ , fileModuleName+ , canonicalizeModulePath+ , homeModuleGraph+ , updateHomeModuleGraph+ , canonicalizeModuleGraph+ , reachable+ , moduleGraphToDot+ ) where++import DriverPipeline+import DynFlags+import ErrUtils+import Exception+import Finder+import GHC+import HscTypes+import Pretty++import Control.Arrow ((&&&))+import Control.Applicative+import Control.Monad+import Control.Monad.Trans.Maybe (MaybeT(..), runMaybeT)+import Control.Monad.State.Strict (execStateT)+import Control.Monad.State.Class+import Data.Maybe+import Data.Monoid as Monoid+import Data.Map (Map)+import qualified Data.Map as Map+import Data.Set (Set)+import qualified Data.Set as Set+import System.FilePath+import System.Directory+import System.IO+import Prelude++import GhcMod.Logging+import GhcMod.Logger+import GhcMod.Monad.Types+import GhcMod.Types+import GhcMod.Utils (withMappedFile)+import GhcMod.Gap (parseModuleHeader)++-- | Turn module graph into a graphviz dot file+--+-- @dot -Tpng -o modules.png modules.dot@+moduleGraphToDot :: GmModuleGraph -> String+moduleGraphToDot GmModuleGraph { gmgGraph } =+ "digraph {\n" ++ concatMap edges (Map.toList graph) ++ "}\n"+ where+ graph = Map.map (Set.mapMonotonic mpPath)+ $ Map.mapKeysMonotonic mpPath gmgGraph+ edges :: (FilePath, (Set FilePath)) -> String+ edges (f, sf) =+ concatMap (\f' -> " \""++ f ++"\" -> \""++ f' ++"\"\n") (Set.toList sf)++data S = S {+ sErrors :: [(ModulePath, ErrorMessages)],+ sWarnings :: [(ModulePath, WarningMessages)],+ sGraph :: GmModuleGraph+}++defaultS :: S+defaultS = S [] [] mempty++putErr :: MonadState S m+ => (ModulePath, ErrorMessages) -> m ()+putErr e = do+ s <- get+ put s { sErrors = e:sErrors s}++putWarn :: MonadState S m+ => (ModulePath, ErrorMessages) -> m ()+putWarn w = do+ s <- get+ put s { sWarnings = w:sWarnings s}++gmgLookupMP :: MonadState S m => ModulePath -> m (Maybe (Set ModulePath))+gmgLookupMP k = (Map.lookup k . gmgGraph . sGraph) `liftM` get++graphUnion :: MonadState S m => GmModuleGraph -> m ()+graphUnion gmg = do+ s <- get+ put s { sGraph = sGraph s `mappend` gmg }++reachable :: Set ModulePath -> GmModuleGraph -> Set ModulePath+reachable smp0 GmModuleGraph {..} = go smp0+ where+ go smp = let+ δsmp = Set.unions $+ collapseMaybeSet . flip Map.lookup gmgGraph <$> Set.toList smp+ smp' = smp `Set.union` δsmp+ in if smp == smp' then smp' else go smp'++pruneUnreachable :: Set ModulePath -> GmModuleGraph -> GmModuleGraph+pruneUnreachable smp0 gmg@GmModuleGraph {..} = let+ r = reachable smp0 gmg+ in+ GmModuleGraph {+ gmgGraph = Map.filterWithKey (\k _ -> k `Set.member` r) gmgGraph+ }++collapseMaybeSet :: Maybe (Set a) -> Set a+collapseMaybeSet = maybe Set.empty id++homeModuleGraph :: (IOish m, Gm m)+ => HscEnv -> Set ModulePath -> m GmModuleGraph+homeModuleGraph env smp = updateHomeModuleGraph env mempty smp smp++mkMainModulePath :: FilePath -> ModulePath+mkMainModulePath = ModulePath (mkModuleName "Main")++findModulePath :: HscEnv -> ModuleName -> IO (Maybe ModulePath)+findModulePath env mn = do+ fmap (ModulePath mn) <$> find env mn++findModulePathSet :: HscEnv -> [ModuleName] -> IO (Set ModulePath)+findModulePathSet env mns = do+ Set.fromList . catMaybes <$> findModulePath env `mapM` mns++find :: MonadIO m => HscEnv -> ModuleName -> m (Maybe FilePath)+find env mn = liftIO $ do+ res <- findHomeModule env mn+ case res of+ -- TODO: handle SOURCE imports (hs-boot stuff): addBootSuffixLocn loc+ Found loc@ModLocation { ml_hs_file = Just _ } _mod ->+ return $ normalise <$> ml_hs_file loc+ _ -> return Nothing+++canonicalizeModulePath :: ModulePath -> IO ModulePath+canonicalizeModulePath (ModulePath mn fp) = ModulePath mn <$> canonicalizePath fp++canonicalizeModuleGraph :: MonadIO m => GmModuleGraph -> m GmModuleGraph+canonicalizeModuleGraph GmModuleGraph {..} = liftIO $ do+ GmModuleGraph . Map.fromList <$> mapM fmg (Map.toList gmgGraph)+ where+ fmg (mp, smp) = liftM2 (,) (canonicalizeModulePath mp) (Set.fromList <$> mapM canonicalizeModulePath (Set.toList smp))+++updateHomeModuleGraph :: (IOish m, Gm m)+ => HscEnv+ -> GmModuleGraph+ -> Set ModulePath -- ^ Initial set of modules+ -> Set ModulePath -- ^ Updated set of modules+ -> m GmModuleGraph+updateHomeModuleGraph env GmModuleGraph {..} smp sump = do+ -- TODO: It would be good if we could retain information about modules that+ -- stop to compile after we've already successfully parsed them at some+ -- point. Figure out a way to delete the modules about to be updated only+ -- after we're sure they won't fail to parse .. or something. Should probably+ -- push this whole prune logic deep into updateHomeModuleGraph'+ (pruneUnreachable smp . sGraph) `liftM` runS (updateHomeModuleGraph' env sump)+ where+ runS = flip execStateT defaultS { sGraph = graph' }+ graph' = GmModuleGraph {+ gmgGraph = Set.foldr Map.delete gmgGraph sump+ }++mkFileMap :: Set ModulePath -> Map FilePath ModulePath+mkFileMap smp = Map.fromList $ map (mpPath &&& id) $ Set.toList smp++mkModuleMap :: Set ModulePath -> Map ModuleName ModulePath+mkModuleMap smp = Map.fromList $ map (mpModule &&& id) $ Set.toList smp++updateHomeModuleGraph'+ :: forall m. (MonadState S m, IOish m, Gm m)+ => HscEnv+ -> Set ModulePath -- ^ Initial set of modules+ -> m ()+updateHomeModuleGraph' env smp0 = do+ go `mapM_` Set.toList smp0+ where+ go :: ModulePath -> m ()+ go mp = do+ msmp <- gmgLookupMP mp+ case msmp of+ Just _ -> return ()+ Nothing -> do+ smp <- collapseMaybeSet `liftM` step mp++ graphUnion GmModuleGraph {+ gmgGraph = Map.singleton mp smp+ }++ mapM_ go (Set.toList smp)++ step :: ModulePath -> m (Maybe (Set ModulePath))+ step mp = runMaybeT $ do+ (dflags, ppsrc_fn) <- MaybeT preprocess'+ src <- liftIO $ readFile ppsrc_fn+ imports mp src dflags+ where+ preprocess' :: m (Maybe (DynFlags, FilePath))+ preprocess' = do+ let fn = mpPath mp+ ep <- preprocessFile env fn+ case ep of+ Right (_, x) -> return $ Just x+ Left errs -> do+ -- TODO: Remember these and present them as proper errors if this is+ -- the file the user is looking at.+ gmLog GmWarning ("preprocess " ++ show fn) $ Pretty.empty $+$ (vcat $ map text errs)+ return Nothing+++ imports :: ModulePath -> String -> DynFlags -> MaybeT m (Set ModulePath)+ imports mp@ModulePath {..} src dflags =+ case parseModuleHeader src dflags mpPath of+ Left err -> do+ putErr (mp, err)+ mzero++ Right (ws, lmdl) -> do+ putWarn (mp, ws)+ let HsModule {..} = unLoc lmdl+ mns = map (unLoc . ideclName)+ $ filter (isNothing . ideclPkgQual)+ $ map unLoc hsmodImports+ -- TODO: handle package qualifier "this"+ liftIO $ Set.fromList . catMaybes <$> mapM (findModulePath env) mns++preprocessFile :: (IOish m, GmEnv m, GmState m) =>+ HscEnv -> FilePath -> m (Either [String] ([String], (DynFlags, FilePath)))+preprocessFile env file =+ withLogger' env $ \setDf -> do+ withMappedFile file $ \fn -> do+ let env' = env { hsc_dflags = setDf (hsc_dflags env) }+ liftIO $ preprocess env' (fn, Nothing)++fileModuleName :: (IOish m, GmEnv m, GmState m) =>+ HscEnv -> FilePath -> m (Either [String] (Maybe ModuleName))+fileModuleName env fn = do+ let handler = liftIO . handle (\(_ :: SomeException) -> return $ Right Nothing)+ ep <- preprocessFile env fn+ case ep of+ Left errs -> do+ return $ Left errs+ Right (_warns, (dflags, procdFile)) -> leftM (errBagToStrList env) =<< handler (do+ src <- readFile procdFile+ case parseModuleHeader src dflags procdFile of+ Left errs -> return $ Left errs+ Right (_, lmdl) -> do+ let HsModule {..} = unLoc lmdl+ return $ Right $ unLoc <$> hsmodName)+ where+ leftM f = either (return . Left <=< f) (return . Right)
+ core/GhcMod/LightGhc.hs view
@@ -0,0 +1,60 @@+module GhcMod.LightGhc where++import Control.Monad+import Control.Monad.Reader (runReaderT)+import Data.IORef++import GHC+import GHC.Paths (libdir)+import StaticFlags+import SysTools+import DynFlags+import HscMain+import HscTypes++import GhcMod.Types+import GhcMod.Monad.Types+import GhcMod.DynFlags+import qualified GhcMod.Gap as Gap++-- We have to be more careful about tearing down 'HscEnv's since GHC 8 added an+-- out of process GHCI server which has to be shutdown.+newLightEnv :: IOish m => (DynFlags -> LightGhc DynFlags) -> m HscEnv+newLightEnv mdf = do+ df <- liftIO $ do+ initStaticOpts+ settings <- initSysTools (Just libdir)+ initDynFlags $ defaultDynFlags settings++ hsc_env <- liftIO $ newHscEnv df+ df' <- runLightGhc hsc_env $ mdf df+ return $ hsc_env {+ hsc_dflags = df',+ hsc_IC = (hsc_IC hsc_env) { ic_dflags = df' }+ }++teardownLightEnv :: MonadIO m => HscEnv -> m ()+teardownLightEnv env = runLightGhc env $ do+ Gap.withCleanupSession $ return ()++withLightHscEnv'+ :: IOish m => (DynFlags -> LightGhc DynFlags) -> (HscEnv -> m a) -> m a+withLightHscEnv' mdf action = gbracket (newLightEnv mdf) teardownLightEnv action++withLightHscEnv :: IOish m => [GHCOption] -> (HscEnv -> m a) -> m a+withLightHscEnv opts = withLightHscEnv' (f <=< liftIO . newHscEnv)+ where+ f env = runLightGhc env $ do+ -- HomeModuleGraph and probably all other clients get into all sorts of+ -- trouble if the package state isn't initialized here+ _ <- setSessionDynFlags =<< addCmdOpts opts =<< getSessionDynFlags+ getSessionDynFlags++runLightGhc :: MonadIO m => HscEnv -> LightGhc a -> m a+runLightGhc env action = liftIO $ do+ renv <- newIORef env+ flip runReaderT renv $ unLightGhc action++runLightGhc' :: MonadIO m => IORef HscEnv -> LightGhc a -> m a+runLightGhc' renv action = liftIO $ do+ flip runReaderT renv $ unLightGhc action
+ core/GhcMod/Logger.hs view
@@ -0,0 +1,189 @@+{-# LANGUAGE CPP, RankNTypes #-}++module GhcMod.Logger (+ withLogger+ , withLogger'+ , checkErrorPrefix+ , errsToStr+ , errBagToStrList+ ) where++import Control.Arrow+import Control.Applicative+import Data.Ord+import Data.List+import Data.Maybe+import Data.Function+import Control.Monad.Reader (Reader, ask, runReader)+import Data.IORef (IORef, newIORef, readIORef, writeIORef, modifyIORef)+import System.FilePath (normalise)++import ErrUtils+import GHC+import HscTypes+import Outputable+import qualified GHC as G+import Bag+import SrcLoc+import FastString++import GhcMod.Convert+import GhcMod.Doc (showPage)+import GhcMod.DynFlags (withDynFlags)+import GhcMod.Monad.Types+import GhcMod.Error+import GhcMod.Pretty+import GhcMod.Utils (mkRevRedirMapFunc)+import qualified GhcMod.Gap as Gap+import Prelude++type Builder = [String] -> [String]++data Log = Log [String] Builder++newtype LogRef = LogRef (IORef Log)++data GmPprEnv = GmPprEnv { gpeDynFlags :: DynFlags+ , gpeMapFile :: FilePath -> FilePath+ }++type GmPprEnvM a = Reader GmPprEnv a++emptyLog :: Log+emptyLog = Log [] id++newLogRef :: IO LogRef+newLogRef = LogRef <$> newIORef emptyLog++readAndClearLogRef :: LogRef -> IO [String]+readAndClearLogRef (LogRef ref) = do+ Log _ b <- readIORef ref+ writeIORef ref emptyLog+ return $ b []++appendLogRef :: (FilePath -> FilePath) -> DynFlags -> LogRef -> Gap.GmLogAction+appendLogRef map_file df (LogRef ref) _reason _df sev src st msg = do+ modifyIORef ref update+ where+ -- TODO: get rid of ppMsg and just do more or less what ghc's+ -- defaultLogAction does+ l = ppMsg map_file df st src sev msg++ update lg@(Log ls b)+ | l `elem` ls = lg+ | otherwise = Log (l:ls) (b . (l:))++----------------------------------------------------------------++-- | Logged messages are returned as 'String'.+-- Right is success and Left is failure.+withLogger :: (GmGhc m, GmEnv m, GmOut m, GmState m)+ => (DynFlags -> DynFlags)+ -> m a+ -> m (Either String (String, a))+withLogger f action = do+ env <- G.getSession+ oopts <- outputOpts+ let conv = convert oopts+ eres <- withLogger' env $ \setDf ->+ withDynFlags (f . setDf) action+ return $ either (Left . conv) (Right . first conv) eres++withLogger' :: (IOish m, GmState m, GmEnv m)+ => HscEnv -> ((DynFlags -> DynFlags) -> m a) -> m (Either [String] ([String], a))+withLogger' env action = do+ logref <- liftIO $ newLogRef++ rfm <- mkRevRedirMapFunc++ let setLogger df = Gap.setLogAction df $ appendLogRef rfm df logref+ handlers = [+ GHandler $ \ex -> return $ Left $ runReader (sourceError ex) gpe,+ GHandler $ \ex -> return $ Left [renderGm $ ghcExceptionDoc ex]+ ]+ gpe = GmPprEnv {+ gpeDynFlags = hsc_dflags env+ , gpeMapFile = rfm+ }++ a <- gcatches (Right <$> action setLogger) handlers+ ls <- liftIO $ readAndClearLogRef logref++ return ((,) ls <$> a)++errBagToStrList :: (IOish m, GmState m, GmEnv m) => HscEnv -> Bag ErrMsg -> m [String]+errBagToStrList env errs = do+ rfm <- mkRevRedirMapFunc+ return $ runReader+ (errsToStr (sortMsgBag errs))+ GmPprEnv{ gpeDynFlags = hsc_dflags env, gpeMapFile = rfm }++----------------------------------------------------------------++-- | Converting 'SourceError' to 'String'.+sourceError :: SourceError -> GmPprEnvM [String]+sourceError = errsToStr . sortMsgBag . srcErrorMessages++errsToStr :: [ErrMsg] -> GmPprEnvM [String]+errsToStr = mapM ppErrMsg++sortMsgBag :: Bag ErrMsg -> [ErrMsg]+sortMsgBag bag = sortBy (compare `on` Gap.errorMsgSpan) $ bagToList bag++----------------------------------------------------------------++ppErrMsg :: ErrMsg -> GmPprEnvM String+ppErrMsg err = do+ GmPprEnv {..} <- ask+ let unqual = errMsgContext err+ st = Gap.mkErrStyle' gpeDynFlags unqual+ err' = Gap.setErrorMsgSpan err $ mapSrcSpanFile gpeMapFile (Gap.errorMsgSpan err)+ return $ showPage gpeDynFlags st $ pprLocErrMsg err'++mapSrcSpanFile :: (FilePath -> FilePath) -> SrcSpan -> SrcSpan+mapSrcSpanFile map_file (RealSrcSpan s) =+ RealSrcSpan $ mapRealSrcSpanFile map_file s+mapSrcSpanFile _ (UnhelpfulSpan s) =+ UnhelpfulSpan s++mapRealSrcSpanFile :: (FilePath -> FilePath) -> RealSrcSpan -> RealSrcSpan+mapRealSrcSpanFile map_file s = let+ start = mapRealSrcLocFile map_file $ realSrcSpanStart s+ end = mapRealSrcLocFile map_file $ realSrcSpanEnd s+ in+ mkRealSrcSpan start end++mapRealSrcLocFile :: (FilePath -> FilePath) -> RealSrcLoc -> RealSrcLoc+mapRealSrcLocFile map_file l = let+ file = mkFastString $ map_file $ unpackFS $ srcLocFile l+ line = srcLocLine l+ col = srcLocCol l+ in+ mkRealSrcLoc file line col++ppMsg :: (FilePath -> FilePath) -> DynFlags -> PprStyle -> SrcSpan -> Severity -> SDoc -> String+ppMsg map_file df st spn sev msg = let+ cts = showPage df st msg+ in+ ppMsgPrefix map_file df spn sev cts ++ cts++ppMsgPrefix :: (FilePath -> FilePath) -> DynFlags -> SrcSpan -> Severity -> String -> String+ppMsgPrefix map_file df spn sev cts =+ let+ defaultPrefix = if Gap.isDumpSplices df then "" else checkErrorPrefix+ in+ fromMaybe defaultPrefix $ do+ (line,col,_,_) <- Gap.getSrcSpan spn+ file <- map_file <$> normalise <$> Gap.getSrcFile spn+ return $ file ++ ":" ++ show line ++ ":" ++ show col ++ ":" +++ if or (map (\x -> x `isPrefixOf` cts) warningAsErrorPrefixes)+ then ""+ else Gap.showSeverityCaption sev++checkErrorPrefix :: String+checkErrorPrefix = "Dummy:0:0:Error:"++warningAsErrorPrefixes :: [String]+warningAsErrorPrefixes = [ "Couldn't match expected type"+ , "Couldn't match type"+ , "No instance for"]
+ core/GhcMod/Logging.hs view
@@ -0,0 +1,115 @@+-- ghc-mod: Happy Haskell Hacking+-- Copyright (C) 2015 Daniel Gröber <dxld ÄT darkboxed DOT org>+--+-- This program is free software: you can redistribute it and/or modify+-- it under the terms of the GNU Affero General Public License as published by+-- the Free Software Foundation, either version 3 of the License, or+-- (at your option) any later version.+--+-- This program is distributed in the hope that it will be useful,+-- but WITHOUT ANY WARRANTY; without even the implied warranty of+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+-- GNU Affero General Public License for more details.+--+-- You should have received a copy of the GNU Affero General Public License+-- along with this program. If not, see <http://www.gnu.org/licenses/>.++{-# LANGUAGE GeneralizedNewtypeDeriving #-}++module GhcMod.Logging (+ module GhcMod.Logging+ , module GhcMod.Pretty+ , GmLogLevel(..)+ , module Data.Monoid+ , module Pretty+ ) where++import Control.Applicative hiding (empty)+import Control.Monad+import Control.Monad.Trans.Class+import Data.List+import Data.Char+import Data.Monoid+import Data.Maybe+import System.IO+import System.FilePath+import Prelude++import Pretty hiding (style, (<>))++import GhcMod.Monad.Types+import GhcMod.Types+import GhcMod.Pretty+import GhcMod.Output++gmSetLogLevel :: GmLog m => GmLogLevel -> m ()+gmSetLogLevel level =+ gmlJournal $ GhcModLog (Just level) (Last Nothing) []++gmGetLogLevel :: forall m. GmLog m => m GmLogLevel+gmGetLogLevel = do+ GhcModLog { gmLogLevel = Just level } <- gmlHistory+ return level++gmSetDumpLevel :: GmLog m => Bool -> m ()+gmSetDumpLevel level =+ gmlJournal $ GhcModLog Nothing (Last (Just level)) []+++increaseLogLevel :: GmLogLevel -> GmLogLevel+increaseLogLevel l | l == maxBound = l+increaseLogLevel l = succ l++decreaseLogLevel :: GmLogLevel -> GmLogLevel+decreaseLogLevel l | l == minBound = l+decreaseLogLevel l = pred l++-- |+-- >>> Just GmDebug <= Nothing+-- False+-- >>> Just GmException <= Just GmDebug+-- True+-- >>> Just GmDebug <= Just GmException+-- False+gmLog :: (MonadIO m, GmLog m, GmOut m) => GmLogLevel -> String -> Doc -> m ()+gmLog level loc' doc = do+ GhcModLog { gmLogLevel = Just level' } <- gmlHistory++ let loc | loc' == "" = empty+ | otherwise = text loc' <+>: empty+ msgDoc = sep [loc, doc]+ msg = dropWhileEnd isSpace $ renderGm $ gmLogLevelDoc level <+>: msgDoc++ when (level <= level') $ gmErrStrLn msg+ gmLogQuiet level loc' doc++gmLogQuiet :: GmLog m => GmLogLevel -> String -> Doc -> m ()+gmLogQuiet level loc doc =+ gmlJournal (GhcModLog Nothing (Last Nothing) [(level, loc, doc)])++gmAppendLogQuiet :: GmLog m => GhcModLog -> m ()+gmAppendLogQuiet GhcModLog { gmLogMessages } =+ forM_ gmLogMessages $ \(level, loc, doc) -> gmLogQuiet level loc doc++gmVomit :: (MonadIO m, GmLog m, GmOut m, GmEnv m) => String -> Doc -> String -> m ()+gmVomit filename doc content = do+ gmLog GmVomit "" $ doc <+>: text content++ GhcModLog { gmLogVomitDump = Last mdump }+ <- gmlHistory++ dir <- cradleTempDir `liftM` cradle+ when (fromMaybe False mdump) $+ liftIO $ writeFile (dir </> filename) content+++newtype LogDiscardT m a = LogDiscardT { runLogDiscard :: m a }+ deriving (Functor, Applicative, Monad)++instance MonadTrans LogDiscardT where+ lift = LogDiscardT++instance Monad m => GmLog (LogDiscardT m) where+ gmlJournal = const $ return ()+ gmlHistory = return mempty+ gmlClear = return ()
+ core/GhcMod/Monad.hs view
@@ -0,0 +1,148 @@+-- ghc-mod: Happy Haskell Hacking+-- Copyright (C) 2015 Daniel Gröber <dxld ÄT darkboxed DOT org>+--+-- This program is free software: you can redistribute it and/or modify+-- it under the terms of the GNU Affero General Public License as published by+-- the Free Software Foundation, either version 3 of the License, or+-- (at your option) any later version.+--+-- This program is distributed in the hope that it will be useful,+-- but WITHOUT ANY WARRANTY; without even the implied warranty of+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+-- GNU Affero General Public License for more details.+--+-- You should have received a copy of the GNU Affero General Public License+-- along with this program. If not, see <http://www.gnu.org/licenses/>.++{-# LANGUAGE CPP #-}+module GhcMod.Monad (+ runGmOutT+ , runGmOutT'+ , runGhcModT+ , runGhcModT'+ , hoistGhcModT+ , runGmlT+ , runGmlT'+ , runGmlTWith+ , runGmPkgGhc+ , withGhcModEnv+ , withGhcModEnv'+ , module GhcMod.Monad.Types+ ) where++import GhcMod.Types+import GhcMod.Monad.Types+import GhcMod.Error+import GhcMod.Logging+import GhcMod.Cradle+import GhcMod.Target+import GhcMod.Output++import Control.Arrow (first)+import Control.Applicative++import Control.Concurrent++import Control.Monad.Reader (runReaderT)+import Control.Monad.State.Strict (runStateT)+import Control.Monad.Trans.Journal (runJournalT)++import Exception++import System.Directory+import System.IO.Unsafe+import Prelude++withGhcModEnv :: (IOish m, GmOut m) => FilePath -> Options -> ((GhcModEnv, GhcModLog) -> m a) -> m a+withGhcModEnv dir opts f = withGhcModEnv' withCradle dir opts f+ where+ withCradle dir' =+ gbracket+ (runJournalT $ do+ gmSetLogLevel $ ooptLogLevel $ optOutput opts+ findCradle' (optPrograms opts) dir')+ (liftIO . cleanupCradle . fst)++++cwdLock :: MVar ThreadId+cwdLock = unsafePerformIO $ newEmptyMVar+{-# NOINLINE cwdLock #-}++withGhcModEnv' :: (IOish m, GmOut m) => (FilePath -> ((Cradle, GhcModLog) -> m a) -> m a) -> FilePath -> Options -> ((GhcModEnv, GhcModLog) -> m a) -> m a+withGhcModEnv' withCradle dir opts f =+ withCradle dir $ \(crdl,lg) ->+ withCradleRootDir crdl $+ f (GhcModEnv opts crdl, lg)+ where+ swapCurrentDirectory ndir = do+ odir <- canonicalizePath =<< getCurrentDirectory+ setCurrentDirectory ndir+ return odir++ withCradleRootDir (cradleRootDir -> projdir) a = do+ success <- liftIO $ tryPutMVar cwdLock =<< myThreadId+ if not success+ then error "withGhcModEnv': using ghc-mod from multiple threads is not supported!"+ else gbracket setup teardown (const a)+ where+ setup = liftIO $ swapCurrentDirectory projdir++ teardown odir = liftIO $ do+ setCurrentDirectory odir+ void $ takeMVar cwdLock++runGmOutT :: IOish m => Options -> GmOutT m a -> m a+runGmOutT opts ma = do+ gmo@GhcModOut{..} <- GhcModOut (optOutput opts) <$> liftIO newChan+ let action = runGmOutT' gmo ma+ case ooptLinePrefix $ optOutput opts of+ Nothing -> action+ Just pfxs ->+ gbracket_ (liftIO $ forkIO $ stdoutGateway pfxs gmoChan)+ (const $ liftIO $ flushStdoutGateway gmoChan)+ action++runGmOutT' :: GhcModOut -> GmOutT m a -> m a+runGmOutT' gmo ma = flip runReaderT gmo $ unGmOutT ma++-- | Run a @GhcModT m@ computation.+runGhcModT :: IOish m+ => Options+ -> GhcModT m a+ -> m (Either GhcModError a, GhcModLog)+runGhcModT opt action = liftIO (getCurrentDirectory >>= canonicalizePath) >>= \dir' -> do+ runGmOutT opt $+ withGhcModEnv dir' opt $ \(env,lg) ->+ first (fst <$>) <$> runGhcModT' env defaultGhcModState (do+ gmSetLogLevel (ooptLogLevel $ optOutput opt)+ gmAppendLogQuiet lg+ action)++-- | @hoistGhcModT result@. Embed a GhcModT computation's result into a GhcModT+-- computation. Note that if the computation that returned @result@ modified the+-- state part of GhcModT this cannot be restored.+hoistGhcModT :: IOish m+ => (Either GhcModError a, GhcModLog)+ -> GhcModT m a+hoistGhcModT (r,l) = do+ gmlJournal l >> case r of+ Left e -> throwError e+ Right a -> return a+++-- | Run a computation inside @GhcModT@ providing the RWST environment and+-- initial state. This is a low level function, use it only if you know what to+-- do with 'GhcModEnv' and 'GhcModState'.+--+-- You should probably look at 'runGhcModT' instead.+runGhcModT' :: IOish m+ => GhcModEnv+ -> GhcModState+ -> GhcModT m a+ -> GmOutT m (Either GhcModError (a, GhcModState), GhcModLog)+runGhcModT' r s a = do+ flip runReaderT r $ runJournalT $ runErrorT $ runStateT (unGmT a) s++gbracket_ :: ExceptionMonad m => m a -> (a -> m b) -> m c -> m c+gbracket_ ma mb mc = gbracket ma mb (const mc)
+ core/GhcMod/Monad/Compat.hs_h view
@@ -0,0 +1,28 @@+-- ghc-mod: Making Haskell development *more* fun+-- Copyright (C) 2015,2016 Daniel Gröber <dxld ÄT darkboxed DOT org>+--+-- This program is free software: you can redistribute it and/or modify+-- it under the terms of the GNU Affero General Public License as published by+-- the Free Software Foundation, either version 3 of the License, or+-- (at your option) any later version.+--+-- This program is distributed in the hope that it will be useful,+-- but WITHOUT ANY WARRANTY; without even the implied warranty of+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+-- GNU Affero General Public License for more details.+--+-- You should have received a copy of the GNU Affero General Public License+-- along with this program. If not, see <http://www.gnu.org/licenses/>.++-- MonadUtils of GHC 7.6 or earlier defines its own MonadIO.+-- RWST does not automatically become an instance of MonadIO.+-- MonadUtils of GHC 7.8 or later imports MonadIO in Monad.Control.IO.Class.+-- So, RWST automatically becomes an instance of+#if __GLASGOW_HASKELL__ < 708+-- 'CoreMonad.MonadIO' and 'Control.Monad.IO.Class.MonadIO' are different+-- classes before ghc 7.8+#define DIFFERENT_MONADIO 1++-- RWST doen't have a MonadIO instance before ghc 7.8+#define MONADIO_INSTANCES 1+#endif
+ core/GhcMod/Monad/Env.hs view
@@ -0,0 +1,68 @@+-- ghc-mod: Happy Haskell Hacking+-- Copyright (C) 2015,2016 Daniel Gröber <dxld ÄT darkboxed DOT org>+--+-- This program is free software: you can redistribute it and/or modify+-- it under the terms of the GNU Affero General Public License as published by+-- the Free Software Foundation, either version 3 of the License, or+-- (at your option) any later version.+--+-- This program is distributed in the hope that it will be useful,+-- but WITHOUT ANY WARRANTY; without even the implied warranty of+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+-- GNU Affero General Public License for more details.+--+-- You should have received a copy of the GNU Affero General Public License+-- along with this program. If not, see <http://www.gnu.org/licenses/>.++{-# LANGUAGE GeneralizedNewtypeDeriving, StandaloneDeriving #-}+{-# LANGUAGE FlexibleInstances, UndecidableInstances #-}++module GhcMod.Monad.Env where++import GhcMod.Types+import GhcMod.Monad.Newtypes++import Control.Monad+import Control.Monad.Trans.Journal (JournalT)+import Control.Monad.State.Strict (StateT(..))+import Control.Monad.Error (ErrorT(..))+import Control.Monad.Reader.Class+import Control.Monad.Trans.Class (MonadTrans(..))+import Prelude++class Monad m => GmEnv m where+ gmeAsk :: m GhcModEnv+ gmeAsk = gmeReader id++ gmeReader :: (GhcModEnv -> a) -> m a+ gmeReader f = f `liftM` gmeAsk++ gmeLocal :: (GhcModEnv -> GhcModEnv) -> m a -> m a+ {-# MINIMAL (gmeAsk | gmeReader), gmeLocal #-}++instance Monad m => GmEnv (GmT m) where+ gmeAsk = GmT ask+ gmeReader = GmT . reader+ gmeLocal f a = GmT $ local f (unGmT a)++instance GmEnv m => GmEnv (GmOutT m) where+ gmeAsk = lift gmeAsk+ gmeReader = lift . gmeReader+ gmeLocal f ma = gmLiftWithInner (\run -> gmeLocal f (run ma))++instance GmEnv m => GmEnv (StateT s m) where+ gmeAsk = lift gmeAsk+ gmeReader = lift . gmeReader+ gmeLocal f ma = gmLiftWithInner (\run -> gmeLocal f (run ma))++instance GmEnv m => GmEnv (JournalT GhcModLog m) where+ gmeAsk = lift gmeAsk+ gmeReader = lift . gmeReader+ gmeLocal f ma = gmLiftWithInner (\run -> gmeLocal f (run ma))++instance GmEnv m => GmEnv (ErrorT GhcModError m) where+ gmeAsk = lift gmeAsk+ gmeReader = lift . gmeReader+ gmeLocal f ma = gmLiftWithInner (\run -> gmeLocal f (run ma))++deriving instance (Monad m, GmEnv (GhcModT m)) => GmEnv (GmlT m)
+ core/GhcMod/Monad/Log.hs view
@@ -0,0 +1,71 @@+-- ghc-mod: Happy Haskell Hacking+-- Copyright (C) 2015,2016 Daniel Gröber <dxld ÄT darkboxed DOT org>+--+-- This program is free software: you can redistribute it and/or modify+-- it under the terms of the GNU Affero General Public License as published by+-- the Free Software Foundation, either version 3 of the License, or+-- (at your option) any later version.+--+-- This program is distributed in the hope that it will be useful,+-- but WITHOUT ANY WARRANTY; without even the implied warranty of+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+-- GNU Affero General Public License for more details.+--+-- You should have received a copy of the GNU Affero General Public License+-- along with this program. If not, see <http://www.gnu.org/licenses/>.++{-# LANGUAGE GeneralizedNewtypeDeriving, StandaloneDeriving #-}+{-# LANGUAGE FlexibleInstances, UndecidableInstances #-}++module GhcMod.Monad.Log where++import GhcMod.Types+import GhcMod.Monad.Newtypes++import Control.Monad+import Control.Monad.Trans.Journal (JournalT)+import Control.Monad.Reader (ReaderT(..))+import Control.Monad.State.Strict (StateT(..))+import Control.Monad.Error (Error, ErrorT(..))+import Control.Monad.Trans.Maybe (MaybeT(..))+import Control.Monad.Journal.Class (MonadJournal(..))+import Control.Monad.Trans.Class (MonadTrans(..))+import Prelude++class Monad m => GmLog m where+ gmlJournal :: GhcModLog -> m ()+ gmlHistory :: m GhcModLog+ gmlClear :: m ()++instance Monad m => GmLog (JournalT GhcModLog m) where+ gmlJournal = journal+ gmlHistory = history+ gmlClear = clear++instance Monad m => GmLog (GmT m) where+ gmlJournal = GmT . lift . lift . journal+ gmlHistory = GmT $ lift $ lift history+ gmlClear = GmT $ lift $ lift clear++instance (Monad m, GmLog m) => GmLog (ReaderT r m) where+ gmlJournal = lift . gmlJournal+ gmlHistory = lift gmlHistory+ gmlClear = lift gmlClear++instance (Monad m, GmLog m) => GmLog (StateT s m) where+ gmlJournal = lift . gmlJournal+ gmlHistory = lift gmlHistory+ gmlClear = lift gmlClear++instance (Monad m, GmLog m, Error e) => GmLog (ErrorT e m) where+ gmlJournal = lift . gmlJournal+ gmlHistory = lift gmlHistory+ gmlClear = lift gmlClear++instance (Monad m, GmLog m) => GmLog (MaybeT m) where+ gmlJournal = lift . gmlJournal+ gmlHistory = lift gmlHistory+ gmlClear = lift gmlClear++deriving instance GmLog m => GmLog (GmOutT m)+deriving instance (Monad m, GmLog (GhcModT m)) => GmLog (GmlT m)
+ core/GhcMod/Monad/Newtypes.hs view
@@ -0,0 +1,176 @@+-- ghc-mod: Happy Haskell Hacking+-- Copyright (C) 2015,2016 Daniel Gröber <dxld ÄT darkboxed DOT org>+--+-- This program is free software: you can redistribute it and/or modify+-- it under the terms of the GNU Affero General Public License as published by+-- the Free Software Foundation, either version 3 of the License, or+-- (at your option) any later version.+--+-- This program is distributed in the hope that it will be useful,+-- but WITHOUT ANY WARRANTY; without even the implied warranty of+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+-- GNU Affero General Public License for more details.+--+-- You should have received a copy of the GNU Affero General Public License+-- along with this program. If not, see <http://www.gnu.org/licenses/>.++{-# LANGUAGE CPP, GeneralizedNewtypeDeriving, DeriveFunctor #-}+{-# LANGUAGE MultiParamTypeClasses, TypeFamilies, UndecidableInstances #-}+{-# LANGUAGE RankNTypes, FlexibleInstances #-}++module GhcMod.Monad.Newtypes where++#include "Compat.hs_h"++import GhcMod.Types++import GHC++import Control.Applicative+import Control.Monad++import Control.Monad.Reader (ReaderT(..))+import Control.Monad.Error (ErrorT(..), MonadError(..))+import Control.Monad.State.Strict (StateT(..))+import Control.Monad.Trans.Journal (JournalT)+import Control.Monad.Reader.Class+import Control.Monad.State.Class (MonadState(..))+import Control.Monad.Journal.Class (MonadJournal(..))+import Control.Monad.Trans.Class (MonadTrans(..))+import Control.Monad.Trans.Control+import Control.Monad.Base (MonadBase(..), liftBase)++import Data.IORef+import Prelude++type GhcModT m = GmT (GmOutT m)++newtype GmOutT m a = GmOutT {+ unGmOutT :: ReaderT GhcModOut m a+ } deriving ( Functor+ , Applicative+ , Alternative+ , Monad+ , MonadPlus+ , MonadTrans+ )++newtype GmT m a = GmT {+ unGmT :: StateT GhcModState+ (ErrorT GhcModError+ (JournalT GhcModLog+ (ReaderT GhcModEnv m) ) ) a+ } deriving ( Functor+ , Applicative+ , Alternative+ , Monad+ , MonadPlus+ , MonadError GhcModError+ )++newtype GmlT m a = GmlT { unGmlT :: GhcModT m a }+ deriving ( Functor+ , Applicative+ , Alternative+ , Monad+ , MonadPlus+ , MonadError GhcModError+ )++newtype LightGhc a = LightGhc { unLightGhc :: ReaderT (IORef HscEnv) IO a }+ deriving ( Functor+ , Applicative+ , Monad+ )++-- GmOutT ----------------------------------------+instance (MonadBaseControl IO m) => MonadBase IO (GmOutT m) where+ liftBase = GmOutT . liftBase++instance (MonadBaseControl IO m) => MonadBaseControl IO (GmOutT m) where+ type StM (GmOutT m) a = StM (ReaderT GhcModEnv m) a+ liftBaseWith = defaultLiftBaseWith+ restoreM = defaultRestoreM+ {-# INLINE liftBaseWith #-}+ {-# INLINE restoreM #-}++instance MonadTransControl GmOutT where+ type StT GmOutT a = StT (ReaderT GhcModEnv) a+ liftWith = defaultLiftWith GmOutT unGmOutT+ restoreT = defaultRestoreT GmOutT+++-- GmlT ------------------------------------------+instance (MonadBaseControl IO m) => MonadBase IO (GmlT m) where+ liftBase = GmlT . liftBase++instance (MonadBaseControl IO m) => MonadBaseControl IO (GmlT m) where+ type StM (GmlT m) a = StM (GmT m) a+ liftBaseWith = defaultLiftBaseWith+ restoreM = defaultRestoreM+ {-# INLINE liftBaseWith #-}+ {-# INLINE restoreM #-}++instance MonadTransControl GmlT where+ type StT GmlT a = StT GmT a+ liftWith f = GmlT $+ liftWith $ \runGm ->+ liftWith $ \runEnv ->+ f $ \ma -> runEnv $ runGm $ unGmlT ma+ restoreT = GmlT . restoreT . restoreT++instance MonadTrans GmlT where+ lift = GmlT . lift . lift++-- GmT ------------------------------------------++instance MonadReader r m => MonadReader r (GmT m) where+ local f ma = gmLiftWithInner (\run -> local f (run ma))+ ask = gmLiftInner ask++instance MonadState s m => MonadState s (GmT m) where+ get = GmT $ lift $ lift $ lift get+ put = GmT . lift . lift . lift . put+ state = GmT . lift . lift . lift . state++instance Monad m => MonadJournal GhcModLog (GmT m) where+ journal w = GmT $ lift $ lift $ (journal w)+ history = GmT $ lift $ lift $ history+ clear = GmT $ lift $ lift $ clear++instance (MonadBaseControl IO m) => MonadBase IO (GmT m) where+ liftBase = GmT . liftBase++instance (MonadBaseControl IO m) => MonadBaseControl IO (GmT m) where+ type StM (GmT m) a =+ StM (StateT GhcModState+ (ErrorT GhcModError+ (JournalT GhcModLog+ (ReaderT GhcModEnv m) ) ) ) a+ liftBaseWith f = GmT (liftBaseWith $ \runInBase ->+ f $ runInBase . unGmT)+ restoreM = GmT . restoreM+ {-# INLINE liftBaseWith #-}+ {-# INLINE restoreM #-}++instance MonadTransControl GmT where+ type StT GmT a = (Either GhcModError (a, GhcModState), GhcModLog)+ liftWith f = GmT $+ liftWith $ \runS ->+ liftWith $ \runE ->+ liftWith $ \runJ ->+ liftWith $ \runR ->+ f $ \ma -> runR $ runJ $ runE $ runS $ unGmT ma+ restoreT = GmT . restoreT . restoreT . restoreT . restoreT+ {-# INLINE liftWith #-}+ {-# INLINE restoreT #-}++instance MonadTrans GmT where+ lift = GmT . lift . lift . lift . lift++gmLiftInner :: Monad m => m a -> GmT m a+gmLiftInner = GmT . lift . lift . lift . lift++gmLiftWithInner :: (MonadTransControl t, Monad m, Monad (t m))+ => (Run t -> m (StT t a)) -> t m a+gmLiftWithInner f = liftWith f >>= restoreT . return
+ core/GhcMod/Monad/Orphans.hs view
@@ -0,0 +1,83 @@+-- ghc-mod: Happy Haskell Hacking+-- Copyright (C) 2015,2016 Daniel Gröber <dxld ÄT darkboxed DOT org>+--+-- This program is free software: you can redistribute it and/or modify+-- it under the terms of the GNU Affero General Public License as published by+-- the Free Software Foundation, either version 3 of the License, or+-- (at your option) any later version.+--+-- This program is distributed in the hope that it will be useful,+-- but WITHOUT ANY WARRANTY; without even the implied warranty of+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+-- GNU Affero General Public License for more details.+--+-- You should have received a copy of the GNU Affero General Public License+-- along with this program. If not, see <http://www.gnu.org/licenses/>.++{-# LANGUAGE CPP, UndecidableInstances, StandaloneDeriving #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}++{-# OPTIONS_GHC -fno-warn-orphans #-}+module GhcMod.Monad.Orphans where++#include "Compat.hs_h"++import GhcMod.Types+import GhcMod.Monad.Newtypes++#if DIFFERENT_MONADIO+import qualified MonadUtils as GHC (MonadIO(..))+#endif+import qualified Control.Monad.IO.Class as MTL++import Control.Monad.Reader (ReaderT(..))+import Control.Monad.State.Strict (StateT(..))+import Control.Monad.Trans.Journal (JournalT)+import Control.Monad.Trans.Maybe (MaybeT(..))+import Control.Monad.Error (Error(..), ErrorT(..))++--------------------------------------------------+-- Miscellaneous instances++#if DIFFERENT_MONADIO+instance MTL.MonadIO m => GHC.MonadIO (ReaderT x m) where+ liftIO = MTL.liftIO+instance MTL.MonadIO m => GHC.MonadIO (StateT x m) where+ liftIO = MTL.liftIO+instance (Error e, MTL.MonadIO m) => GHC.MonadIO (ErrorT e m) where+ liftIO = MTL.liftIO+instance MTL.MonadIO m => GHC.MonadIO (JournalT x m) where+ liftIO = MTL.liftIO+instance MTL.MonadIO m => GHC.MonadIO (MaybeT m) where+ liftIO = MTL.liftIO+deriving instance MTL.MonadIO m => GHC.MonadIO (GmOutT m)+deriving instance MTL.MonadIO m => GHC.MonadIO (GmT m)+deriving instance MTL.MonadIO m => GHC.MonadIO (GmlT m)+deriving instance GHC.MonadIO LightGhc+#endif++deriving instance MTL.MonadIO m => MTL.MonadIO (GmOutT m)+deriving instance MTL.MonadIO m => MTL.MonadIO (GmT m)+deriving instance MTL.MonadIO m => MTL.MonadIO (GmlT m)+deriving instance MTL.MonadIO LightGhc++instance MonadIO IO where+ liftIO = id+instance MonadIO m => MonadIO (ReaderT x m) where+ liftIO = MTL.liftIO+instance MonadIO m => MonadIO (StateT x m) where+ liftIO = MTL.liftIO+instance (Error e, MonadIO m) => MonadIO (ErrorT e m) where+ liftIO = MTL.liftIO+instance MonadIO m => MonadIO (JournalT x m) where+ liftIO = MTL.liftIO+instance MonadIO m => MonadIO (MaybeT m) where+ liftIO = MTL.liftIO+instance MonadIOC m => MonadIO (GmOutT m) where+ liftIO = MTL.liftIO+instance MonadIOC m => MonadIO (GmT m) where+ liftIO = MTL.liftIO+instance MonadIOC m => MonadIO (GmlT m) where+ liftIO = MTL.liftIO+instance MonadIO LightGhc where+ liftIO = MTL.liftIO
+ core/GhcMod/Monad/Out.hs view
@@ -0,0 +1,52 @@+-- ghc-mod: Happy Haskell Hacking+-- Copyright (C) 2015,2016 Daniel Gröber <dxld ÄT darkboxed DOT org>+--+-- This program is free software: you can redistribute it and/or modify+-- it under the terms of the GNU Affero General Public License as published by+-- the Free Software Foundation, either version 3 of the License, or+-- (at your option) any later version.+--+-- This program is distributed in the hope that it will be useful,+-- but WITHOUT ANY WARRANTY; without even the implied warranty of+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+-- GNU Affero General Public License for more details.+--+-- You should have received a copy of the GNU Affero General Public License+-- along with this program. If not, see <http://www.gnu.org/licenses/>.++{-# LANGUAGE GeneralizedNewtypeDeriving, StandaloneDeriving #-}+{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, UndecidableInstances #-}++module GhcMod.Monad.Out where++import GhcMod.Types+import GhcMod.Monad.Newtypes++import Control.Monad+import Control.Monad.State.Strict (StateT(..))+import Control.Monad.Trans.Journal (JournalT)+import Control.Monad.Trans.Maybe (MaybeT(..))+import Control.Monad.Reader.Class+import Control.Monad.Trans.Class (MonadTrans(..))+import Prelude++class Monad m => GmOut m where+ gmoAsk :: m GhcModOut++instance Monad m => GmOut (GmOutT m) where+ gmoAsk = GmOutT ask++instance Monad m => GmOut (GmlT m) where+ gmoAsk = GmlT $ lift $ GmOutT ask++instance GmOut m => GmOut (GmT m) where+ gmoAsk = lift gmoAsk++instance GmOut m => GmOut (StateT s m) where+ gmoAsk = lift gmoAsk++instance GmOut m => GmOut (JournalT w m) where+ gmoAsk = lift gmoAsk++instance GmOut m => GmOut (MaybeT m) where+ gmoAsk = lift gmoAsk
+ core/GhcMod/Monad/State.hs view
@@ -0,0 +1,67 @@+-- ghc-mod: Happy Haskell Hacking+-- Copyright (C) 2015,2016 Daniel Gröber <dxld ÄT darkboxed DOT org>+--+-- This program is free software: you can redistribute it and/or modify+-- it under the terms of the GNU Affero General Public License as published by+-- the Free Software Foundation, either version 3 of the License, or+-- (at your option) any later version.+--+-- This program is distributed in the hope that it will be useful,+-- but WITHOUT ANY WARRANTY; without even the implied warranty of+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+-- GNU Affero General Public License for more details.+--+-- You should have received a copy of the GNU Affero General Public License+-- along with this program. If not, see <http://www.gnu.org/licenses/>.++{-# LANGUAGE GeneralizedNewtypeDeriving, StandaloneDeriving #-}+{-# LANGUAGE FlexibleInstances, UndecidableInstances #-}++module GhcMod.Monad.State where++import GhcMod.Types+import GhcMod.Monad.Newtypes++import Control.Monad+import Control.Monad.State.Strict (StateT(..))+import Control.Monad.Trans.Maybe (MaybeT(..))+import Control.Monad.State.Class (MonadState(..))+import Control.Monad.Trans.Class (MonadTrans(..))+import Prelude++class Monad m => GmState m where+ gmsGet :: m GhcModState+ gmsGet = gmsState (\s -> (s, s))++ gmsPut :: GhcModState -> m ()+ gmsPut s = gmsState (\_ -> ((), s))++ gmsState :: (GhcModState -> (a, GhcModState)) -> m a+ gmsState f = do+ s <- gmsGet+ let ~(a, s') = f s+ gmsPut s'+ return a+ {-# MINIMAL gmsState | gmsGet, gmsPut #-}++instance GmState m => GmState (StateT s m) where+ gmsGet = lift gmsGet+ gmsPut = lift . gmsPut+ gmsState = lift . gmsState++instance Monad m => GmState (StateT GhcModState m) where+ gmsGet = get+ gmsPut = put+ gmsState = state++instance Monad m => GmState (GmT m) where+ gmsGet = GmT get+ gmsPut = GmT . put+ gmsState = GmT . state++instance GmState m => GmState (MaybeT m) where+ gmsGet = MaybeT $ Just `liftM` gmsGet+ gmsPut = MaybeT . (Just `liftM`) . gmsPut+ gmsState = MaybeT . (Just `liftM`) . gmsState++deriving instance (Monad m, GmState (GhcModT m)) => GmState (GmlT m)
+ core/GhcMod/Monad/Types.hs view
@@ -0,0 +1,239 @@+-- ghc-mod: Happy Haskell Hacking+-- Copyright (C) 2015 Daniel Gröber <dxld ÄT darkboxed DOT org>+--+-- This program is free software: you can redistribute it and/or modify+-- it under the terms of the GNU Affero General Public License as published by+-- the Free Software Foundation, either version 3 of the License, or+-- (at your option) any later version.+--+-- This program is distributed in the hope that it will be useful,+-- but WITHOUT ANY WARRANTY; without even the implied warranty of+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+-- GNU Affero General Public License for more details.+--+-- You should have received a copy of the GNU Affero General Public License+-- along with this program. If not, see <http://www.gnu.org/licenses/>.++{-# LANGUAGE CPP, GeneralizedNewtypeDeriving, FlexibleInstances #-}+{-# LANGUAGE FlexibleContexts, MultiParamTypeClasses, RankNTypes #-}+{-# LANGUAGE TypeFamilies, UndecidableInstances, BangPatterns #-}+{-# LANGUAGE StandaloneDeriving #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}++module GhcMod.Monad.Types (+ -- * Monad Types+ GhcModT+ , GmOutT(..)+ , GmT(..)+ , GmlT(..)+ , LightGhc(..)+ , GmGhc+ , IOish+ -- * Environment, state and logging+ , GhcModEnv(..)+ , GhcModState(..)+ , GhcModCaches(..)+ , defaultGhcModState+ , GmGhcSession(..)+ , GmComponent(..)+ -- * Accessing 'GhcModEnv', 'GhcModState' and 'GhcModLog'+ , GmLogLevel(..)+ , GhcModLog(..)+ , GhcModError(..)+ , Gm+ , GmEnv(..)+ , GmState(..)+ , GmLog(..)+ , GmOut(..)+ , cradle+ , options+ , outputOpts+ , withOptions+ , getMMappedFiles+ , setMMappedFiles+ , addMMappedFile+ , delMMappedFile+ , lookupMMappedFile+ , getMMappedFilePaths+ -- * Re-exporting convenient stuff+ , MonadIO+ , liftIO+ , gmlGetSession+ , gmlSetSession+ ) where++#include "Compat.hs_h"++import GhcMod.Types++import GhcMod.Monad.Env+import GhcMod.Monad.State+import GhcMod.Monad.Log+import GhcMod.Monad.Out+import GhcMod.Monad.Newtypes+import GhcMod.Monad.Orphans ()++import Safe++import GHC+import DynFlags+import Exception+import HscTypes++import Control.Applicative+import Control.Monad++import Control.Monad.Reader (ReaderT(..))+import Control.Monad.State.Strict (StateT(..))+import Control.Monad.Trans.Journal (JournalT)+import Control.Monad.Trans.Maybe (MaybeT)++import Control.Monad.Trans.Control++import Control.Monad.Reader.Class++import qualified Data.Map as M+import Data.Maybe+import Data.Monoid+import Data.IORef+import Prelude++type Gm m = (GmEnv m, GmState m, GmLog m, GmOut m)++--------------------------------------------------+-- GHC API instances -----------------------------++-- GHC cannot prove the following instances to be decidable automatically using+-- the FlexibleContexts extension as they violate the second Paterson Condition,+-- namely that: The assertion has fewer constructors and variables (taken+-- together and counting repetitions) than the head. Specifically the+-- @MonadBaseControl IO m@ constraint in 'IOish' is causing this violation.++type GmGhc m = (IOish m, GhcMonad m)++instance (MonadIO m, MonadBaseControl IO m) => GhcMonad (GmlT m) where+ getSession = gmlGetSession+ setSession = gmlSetSession++-- | Get the underlying GHC session+gmlGetSession :: (MonadIO m, MonadBaseControl IO m) => GmlT m HscEnv+gmlGetSession = do+ ref <- gmgsSession . fromJustNote "gmlGetSession" . gmGhcSession <$> gmsGet+ liftIO $ readIORef ref++-- | Set the underlying GHC session+gmlSetSession :: (MonadIO m, MonadBaseControl IO m) => HscEnv -> GmlT m ()+gmlSetSession a = do+ ref <- gmgsSession . fromJustNote "gmlSetSession" . gmGhcSession <$> gmsGet+ liftIO $ flip writeIORef a ref++instance GhcMonad LightGhc where+ getSession = (liftIO . readIORef) =<< LightGhc ask+ setSession a = (liftIO . flip writeIORef a) =<< LightGhc ask++#if __GLASGOW_HASKELL__ >= 706+instance (MonadIO m, MonadBaseControl IO m) => HasDynFlags (GmlT m) where+ getDynFlags = hsc_dflags <$> getSession++instance HasDynFlags LightGhc where+ getDynFlags = hsc_dflags <$> getSession+#endif++instance (MonadIO m, MonadBaseControl IO m) => ExceptionMonad (GmOutT m) where+ gcatch act handler = control $ \run ->+ run act `gcatch` (run . handler)++ gmask = liftBaseOp gmask . liftRestore+ where liftRestore f r = f $ liftBaseOp_ r++instance (MonadIO m, MonadBaseControl IO m) => ExceptionMonad (GmT m) where+ gcatch act handler = control $ \run ->+ run act `gcatch` (run . handler)++ gmask = liftBaseOp gmask . liftRestore+ where liftRestore f r = f $ liftBaseOp_ r++instance (MonadIO m, MonadBaseControl IO m) => ExceptionMonad (GmlT m) where+ gcatch act handler = control $ \run ->+ run act `gcatch` (run . handler)++ gmask = liftBaseOp gmask . liftRestore+ where liftRestore f r = f $ liftBaseOp_ r++instance ExceptionMonad LightGhc where+ gcatch act handl =+ LightGhc $ unLightGhc act `gcatch` \e -> unLightGhc (handl e)+ gmask f =+ LightGhc $ gmask $ \io_restore ->let+ g_restore (LightGhc m) = LightGhc $ io_restore m+ in+ unLightGhc (f g_restore)+++instance (MonadIO m, MonadBaseControl IO m) => ExceptionMonad (StateT s m) where+ gcatch act handler = control $ \run ->+ run act `gcatch` (run . handler)++ gmask = liftBaseOp gmask . liftRestore+ where liftRestore f r = f $ liftBaseOp_ r++instance (MonadIO m, MonadBaseControl IO m) => ExceptionMonad (ReaderT s m) where+ gcatch act handler = control $ \run ->+ run act `gcatch` (run . handler)++ gmask = liftBaseOp gmask . liftRestore+ where liftRestore f r = f $ liftBaseOp_ r++instance (Monoid w, MonadIO m, MonadBaseControl IO m) => ExceptionMonad (JournalT w m) where+ gcatch act handler = control $ \run ->+ run act `gcatch` (run . handler)++ gmask = liftBaseOp gmask . liftRestore+ where liftRestore f r = f $ liftBaseOp_ r++instance (MonadIO m, MonadBaseControl IO m) => ExceptionMonad (MaybeT m) where+ gcatch act handler = control $ \run ->+ run act `gcatch` (run . handler)++ gmask = liftBaseOp gmask . liftRestore+ where liftRestore f r = f $ liftBaseOp_ r+++----------------------------------------------------------------++options :: GmEnv m => m Options+options = gmOptions `liftM` gmeAsk++outputOpts :: GmOut m => m OutputOpts+outputOpts = gmoOptions `liftM` gmoAsk++cradle :: GmEnv m => m Cradle+cradle = gmCradle `liftM` gmeAsk++getMMappedFiles :: GmState m => m FileMappingMap+getMMappedFiles = gmMMappedFiles `liftM` gmsGet++setMMappedFiles :: GmState m => FileMappingMap -> m ()+setMMappedFiles mf = (\s -> gmsPut s { gmMMappedFiles = mf } ) =<< gmsGet++addMMappedFile :: GmState m => FilePath -> FileMapping -> m ()+addMMappedFile t fm =+ getMMappedFiles >>= setMMappedFiles . M.insert t fm++delMMappedFile :: GmState m => FilePath -> m ()+delMMappedFile t =+ getMMappedFiles >>= setMMappedFiles . M.delete t++lookupMMappedFile :: GmState m => FilePath -> m (Maybe FileMapping)+lookupMMappedFile t =+ M.lookup t `liftM` getMMappedFiles++getMMappedFilePaths :: GmState m => m [FilePath]+getMMappedFilePaths = M.keys `liftM` getMMappedFiles++withOptions :: GmEnv m => (Options -> Options) -> m a -> m a+withOptions changeOpt action = gmeLocal changeEnv action+ where+ changeEnv e = e { gmOptions = changeOpt opt }+ where+ opt = gmOptions e
+ core/GhcMod/Options/DocUtils.hs view
@@ -0,0 +1,48 @@+-- ghc-mod: Happy Haskell Hacking+-- Copyright (C) 2015 Nikolay Yakimov <root@livid.pp.ru>+--+-- This program is free software: you can redistribute it and/or modify+-- it under the terms of the GNU Affero General Public License as published by+-- the Free Software Foundation, either version 3 of the License, or+-- (at your option) any later version.+--+-- This program is distributed in the hope that it will be useful,+-- but WITHOUT ANY WARRANTY; without even the implied warranty of+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+-- GNU Affero General Public License for more details.+--+-- You should have received a copy of the GNU Affero General Public License+-- along with this program. If not, see <http://www.gnu.org/licenses/>.++module GhcMod.Options.DocUtils (+ ($$),+ ($$$),+ (<=>),+ (<$$>),+ (<||>)+) where++import Options.Applicative+import Data.Monoid+import Prelude++infixl 6 <||>+infixr 7 <$$>+infixr 7 $$+infixr 8 <=>+infixr 9 $$$++($$) :: (a -> b) -> a -> b+($$) = ($)++($$$) :: (a -> b) -> a -> b+($$$) = ($)++(<||>) :: Alternative a => a b -> a b -> a b+(<||>) = (<|>)++(<=>) :: Monoid m => m -> m -> m+(<=>) = (<>)++(<$$>) :: Functor f => (a -> b) -> f a -> f b+(<$$>) = (<$>)
+ core/GhcMod/Options/Help.hs view
@@ -0,0 +1,79 @@+-- ghc-mod: Happy Haskell Hacking+-- Copyright (C) 2015 Nikolay Yakimov <root@livid.pp.ru>+--+-- This program is free software: you can redistribute it and/or modify+-- it under the terms of the GNU Affero General Public License as published by+-- the Free Software Foundation, either version 3 of the License, or+-- (at your option) any later version.+--+-- This program is distributed in the hope that it will be useful,+-- but WITHOUT ANY WARRANTY; without even the implied warranty of+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+-- GNU Affero General Public License for more details.+--+-- You should have received a copy of the GNU Affero General Public License+-- along with this program. If not, see <http://www.gnu.org/licenses/>.+{-# LANGUAGE OverloadedStrings, FlexibleInstances, GeneralizedNewtypeDeriving #-}++module GhcMod.Options.Help where++import Options.Applicative+import Options.Applicative.Help.Pretty (Doc)+import qualified Options.Applicative.Help.Pretty as PP+import Control.Monad.State+import GHC.Exts( IsString(..) )+import Data.Maybe+import Data.Monoid+import Prelude++newtype MyDocM s a = MyDoc {unwrapState :: State s a}+ deriving (Monad, Functor, Applicative, MonadState s)+type MyDoc = MyDocM (Maybe Doc) ()++instance IsString (MyDocM (Maybe Doc) a) where+ fromString = append . para++instance Monoid (MyDocM (Maybe Doc) ()) where+ mappend a b = append $ doc a <> doc b+ mempty = append PP.empty++para :: String -> Doc+para = PP.fillSep . map PP.text . words++append :: Doc -> MyDocM (Maybe Doc) a+append s = modify m >> return undefined+ where+ m :: Maybe Doc -> Maybe Doc+ m Nothing = Just s+ m (Just old) = Just $ old PP..$. s++infixr 7 \\+(\\) :: MyDoc -> MyDoc -> MyDoc+(\\) a b = append $ doc a PP.<+> doc b++doc :: MyDoc -> Doc+doc = fromMaybe PP.empty . flip execState Nothing . unwrapState++help' :: MyDoc -> Mod f a+help' = helpDoc . Just . doc++desc :: MyDoc -> InfoMod a+desc = footerDoc . Just . doc . indent 2++code :: MyDoc -> MyDoc+code x = do+ _ <- " "+ indent 4 x+ " "++progDesc' :: MyDoc -> InfoMod a+progDesc' = progDescDoc . Just . doc++indent :: Int -> MyDoc -> MyDoc+indent n = append . PP.indent n . doc++int' :: Int -> MyDoc+int' = append . PP.int++para' :: String -> MyDoc+para' = append . para
+ core/GhcMod/Options/Options.hs view
@@ -0,0 +1,177 @@+-- ghc-mod: Happy Haskell Hacking+-- Copyright (C) 2015 Nikolay Yakimov <root@livid.pp.ru>+--+-- This program is free software: you can redistribute it and/or modify+-- it under the terms of the GNU Affero General Public License as published by+-- the Free Software Foundation, either version 3 of the License, or+-- (at your option) any later version.+--+-- This program is distributed in the hope that it will be useful,+-- but WITHOUT ANY WARRANTY; without even the implied warranty of+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+-- GNU Affero General Public License for more details.+--+-- You should have received a copy of the GNU Affero General Public License+-- along with this program. If not, see <http://www.gnu.org/licenses/>.+{-# LANGUAGE OverloadedStrings #-}+{-# OPTIONS_GHC -fno-warn-unused-do-bind #-}++module GhcMod.Options.Options (+ globalArgSpec+ , parseCmdLineOptions+) where++import Options.Applicative+import Options.Applicative.Types+import GhcMod.Types+import Control.Arrow+import Data.Char (toUpper, toLower)+import Data.List (intercalate)+import GhcMod.Read+import GhcMod.Options.DocUtils+import GhcMod.Options.Help+import Data.Monoid+import Prelude++-- | Parse a set of arguments according to the ghc-mod CLI flag spec, producing+-- @Options@ set accordingly.+parseCmdLineOptions :: [String] -> Maybe Options+parseCmdLineOptions = getParseResult . execParserPure (prefs mempty) (info globalArgSpec mempty)++splitOn :: Eq a => a -> [a] -> ([a], [a])+splitOn c = second (drop 1) . break (==c)++logLevelParser :: Parser GmLogLevel+logLevelParser =+ logLevelSwitch <*>+ logLevelOption+ <||> silentSwitch+ where+ logLevelOption =+ option parseLL+ $$ long "verbose"+ <=> metavar "LEVEL"+ <=> value GmWarning+ <=> showDefaultWith showLL+ <=> help' $$$ do+ "Set log level ("+ <> int' (fromEnum (minBound :: GmLogLevel))+ <> "-"+ <> int' (fromEnum (maxBound :: GmLogLevel))+ <> ")"+ "You can also use strings (case-insensitive):"+ para'+ $ intercalate ", "+ $ map showLL ([minBound..maxBound] :: [GmLogLevel])+ logLevelSwitch =+ repeatAp succ' . length <$> many $$ flag' ()+ $$ short 'v'+ <=> help "Increase log level"+ silentSwitch = flag' GmSilent+ $$ long "silent"+ <=> short 's'+ <=> help "Be silent, set log level to 'silent'"+ showLL = drop 2 . map toLower . show+ repeatAp f n = foldr (.) id (replicate n f)+ succ' x | x == maxBound = x+ | otherwise = succ x+ parseLL = do+ v <- readerAsk+ let+ il'= toEnum . min maxBound <$> readMaybe v+ ll' = readMaybe ("Gm" ++ capFirst v)+ maybe (readerError $ "Not a log level \"" ++ v ++ "\"") return $ ll' <|> il'+ capFirst (h:t) = toUpper h : map toLower t+ capFirst [] = []++outputOptsSpec :: Parser OutputOpts+outputOptsSpec = OutputOpts+ <$> logLevelParser+ <*> flag PlainStyle LispStyle+ $$ long "tolisp"+ <=> short 'l'+ <=> help "Format output as an S-Expression"+ <*> LineSeparator <$$> strOption+ $$ long "boundary"+ <=> long "line-separator"+ <=> short 'b'+ <=> metavar "SEP"+ <=> value "\0"+ <=> showDefault+ <=> help "Output line separator"+ <*> optional $$ splitOn ',' <$$> strOption+ $$ long "line-prefix"+ <=> metavar "OUT,ERR"+ <=> help "Output prefixes"++programsArgSpec :: Parser Programs+programsArgSpec = Programs+ <$> strOption+ $$ long "with-ghc"+ <=> value "ghc"+ <=> showDefault+ <=> help "GHC executable to use"+ <*> strOption+ $$ long "with-ghc-pkg"+ <=> value "ghc-pkg"+ <=> showDefault+ <=> help "ghc-pkg executable to use (only needed when guessing from GHC path fails)"+ <*> strOption+ $$ long "with-cabal"+ <=> value "cabal"+ <=> showDefault+ <=> help "cabal-install executable to use"+ <*> strOption+ $$ long "with-stack"+ <=> value "stack"+ <=> showDefault+ <=> help "stack executable to use"++-- | An optparse-applicative @Parser@ sepcification for @Options@ so that+-- applications making use of the ghc-mod API can have a consistent way of+-- parsing global options.+globalArgSpec :: Parser Options+globalArgSpec = Options+ <$> outputOptsSpec+ <*> programsArgSpec+ <*> many $$ strOption+ $$ long "ghcOpt"+ <=> long "ghc-option"+ <=> short 'g'+ <=> metavar "OPT"+ <=> help "Option to be passed to GHC"+ <*> many fileMappingSpec+ <*> strOption+ $$ long "encoding"+ <=> value "UTF-8"+ <=> showDefault+ <=> help "I/O encoding"+ <*> switch+ $$ long "stack-build-deps"+ <=> showDefault+ <=> help "Build dependencies if needed when using stack"+ where+ fileMappingSpec =+ getFileMapping . splitOn '=' <$> strOption+ $$ long "map-file"+ <=> metavar "MAPPING"+ <=> fileMappingHelp+ fileMappingHelp = help' $ do+ "Redirect one file to another"+ "--map-file \"file1.hs=file2.hs\""+ indent 4 $ do+ "can be used to tell ghc-mod"+ \\ "that it should take source code"+ \\ "for `file1.hs` from `file2.hs`."+ "`file1.hs` can be either full path,"+ \\ "or path relative to project root."+ "`file2.hs` has to be either relative to project root,"+ \\ "or full path (preferred)"+ "--map-file \"file.hs\""+ indent 4 $ do+ "can be used to tell ghc-mod that it should take"+ \\ "source code for `file.hs` from stdin. File end"+ \\ "marker is `\\n\\EOT\\n`, i.e. `\\x0A\\x04\\x0A`."+ \\ "`file.hs` may or may not exist, and should be"+ \\ "either full path, or relative to project root."+ getFileMapping = second (\i -> if null i then Nothing else Just i)
+ core/GhcMod/Output.hs view
@@ -0,0 +1,243 @@+-- ghc-mod: Happy Haskell Hacking+-- Copyright (C) 2015 Daniel Gröber <dxld ÄT darkboxed DOT org>+--+-- This program is free software: you can redistribute it and/or modify+-- it under the terms of the GNU Affero General Public License as published by+-- the Free Software Foundation, either version 3 of the License, or+-- (at your option) any later version.+--+-- This program is distributed in the hope that it will be useful,+-- but WITHOUT ANY WARRANTY; without even the implied warranty of+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+-- GNU Affero General Public License for more details.+--+-- You should have received a copy of the GNU Affero General Public License+-- along with this program. If not, see <http://www.gnu.org/licenses/>.++-- Derived from process:System.Process+-- Copyright (c) The University of Glasgow 2004-2008++{-# LANGUAGE FlexibleInstances #-}+module GhcMod.Output (+ gmPutStr+ , gmErrStr+ , gmPutStrLn+ , gmErrStrLn++ , gmPutStrIO+ , gmErrStrIO++ , gmReadProcess+ , gmReadProcess'++ , stdoutGateway+ , flushStdoutGateway+ ) where++import Data.List+import Data.ByteString.Lazy (ByteString)+import qualified Data.ByteString.Lazy as BS+import qualified Data.Label as L+import qualified Data.Label.Base as LB+import System.IO+import System.Exit+import System.Process+import Control.Monad+import Control.Monad.State.Strict+import Control.DeepSeq+import Control.Exception+import Control.Concurrent (forkIO, killThread)+import Control.Concurrent.MVar+import Control.Concurrent.Chan+import Pipes+import Pipes.Lift+import Prelude++import GhcMod.Types hiding (LineSeparator, MonadIO(..))+import GhcMod.Monad.Types hiding (MonadIO(..))+import GhcMod.Gap ()++class ProcessOutput a where+ hGetContents' :: Handle -> IO a++instance ProcessOutput String where+ hGetContents' = hGetContents++instance ProcessOutput ByteString where+ hGetContents' = BS.hGetContents++outputFns :: (GmOut m, MonadIO m')+ => m (String -> m' (), String -> m' ())+outputFns =+ outputFns' `liftM` gmoAsk++outputFns' ::+ MonadIO m => GhcModOut -> (String -> m (), String -> m ())+outputFns' (GhcModOut oopts c) = let+ OutputOpts {..} = oopts+ in+ case ooptLinePrefix of+ Nothing -> stdioOutputFns+ Just _ -> chanOutputFns c++stdioOutputFns :: MonadIO m => (String -> m (), String -> m ())+stdioOutputFns =+ ( liftIO . putStr+ , liftIO . hPutStr stderr+ )++chanOutputFns :: MonadIO m+ => Chan (Either (MVar ()) (GmStream, String))+ -> (String -> m (), String -> m ())+chanOutputFns c = (write GmOutStream, write GmErrStream)+ where+ write stream s = liftIO $ writeChan c $ Right $ (stream,s)++gmPutStr, gmPutStrLn, gmErrStr, gmErrStrLn+ :: (MonadIO m, GmOut m) => String -> m ()++gmPutStr str = do+ putOut <- gmPutStrIO+ putOut str++gmErrStr str = do+ putErr <- gmErrStrIO+ putErr str++gmPutStrLn = gmPutStr . (++"\n")+gmErrStrLn = gmErrStr . (++"\n")++gmPutStrIO, gmErrStrIO :: (GmOut m, MonadIO mi) => m (String -> mi ())++gmPutStrIO = fst `liftM` outputFns+gmErrStrIO = snd `liftM` outputFns+++gmReadProcess :: GmOut m => m (FilePath -> [String] -> String -> IO String)+gmReadProcess = do+ GhcModOut {..} <- gmoAsk+ case ooptLinePrefix gmoOptions of+ Just _ ->+ readProcessStderrChan+ Nothing ->+ return $ readProcess++gmReadProcess' :: GmOut m => m (FilePath -> [String] -> String -> IO ByteString)+gmReadProcess' = readProcessStderrChan++flushStdoutGateway :: Chan (Either (MVar ()) (GmStream, String)) -> IO ()+flushStdoutGateway c = do+ mv <- newEmptyMVar+ writeChan c $ Left mv+ takeMVar mv++type Line = String++stdoutGateway :: (String, String) -> Chan (Either (MVar ()) (GmStream, String)) -> IO ()+stdoutGateway (outPf, errPf) chan = do+ runEffect $ commandProc >-> evalStateP ("","") seperateStreams+ where+ commandProc :: Producer (Either (MVar ()) (GmStream, String)) IO ()+ commandProc = do+ cmd <- liftIO $ readChan chan+ case cmd of+ Left mv -> do+ yield $ Left mv+ Right input -> do+ yield $ Right input+ commandProc++ seperateStreams :: Consumer (Either (MVar ()) (GmStream, String)) (StateT (String, String) IO) ()+ seperateStreams = do+ ecmd <- await+ case ecmd of+ Left mv -> do+ -- flush buffers+ (\s -> lift $ zoom (streamLens s) $ sGetLine Nothing)+ `mapM_` [GmOutStream, GmErrStream]++ liftIO $ putMVar mv ()+ Right (stream, str) -> do+ ls <- lift $ zoom (streamLens stream) $ sGetLine (Just str)+ case ls of+ [] -> return ()+ _ -> liftIO $ putStr $ unlines $ map (streamPf stream++) ls++ liftIO $ hFlush stdout+ seperateStreams++ sGetLine :: (Maybe String) -> StateT String IO [Line]+ sGetLine mstr' = do+ buf <- get+ let mstr = (buf++) `liftM` mstr'+ case mstr of+ Nothing -> put "" >> return [buf]+ Just "" -> return []+ Just s | last s == '\n' -> put "" >> return (lines s)+ | otherwise -> do+ let (p:ls') = reverse $ lines s+ put p+ return $ reverse $ ls'++ streamLens GmOutStream = LB.fst+ streamLens GmErrStream = LB.snd++ streamPf GmOutStream = outPf+ streamPf GmErrStream = errPf++zoom :: Monad m => (f L.:-> o) -> StateT o m a -> StateT f m a+zoom l (StateT a) =+ StateT $ \f -> do+ (a', s') <- a $ L.get l f+ return (a', L.set l s' f)++readProcessStderrChan ::+ (GmOut m, ProcessOutput a, NFData a) => m (FilePath -> [String] -> String -> IO a)+readProcessStderrChan = do+ (_, e :: String -> IO ()) <- outputFns+ return $ readProcessStderrChan' e++readProcessStderrChan' :: (ProcessOutput a, NFData a) =>+ (String -> IO ()) -> FilePath -> [String] -> String -> IO a+readProcessStderrChan' putErr exe args input = do+ let cp = (proc exe args) {+ std_out = CreatePipe+ , std_err = CreatePipe+ , std_in = CreatePipe+ }+ (Just i, Just o, Just e, h) <- createProcess cp++ _ <- forkIO $ reader e++ output <- hGetContents' o+ withForkWait (evaluate $ rnf output) $ \waitOut -> do++ -- now write any input+ unless (null input) $+ ignoreSEx $ hPutStr i input+ -- hClose performs implicit hFlush, and thus may trigger a SIGPIPE+ ignoreSEx $ hClose i++ -- wait on the output+ waitOut+ hClose o++ res <- waitForProcess h+ case res of+ ExitFailure rv ->+ throw $ GMEProcess "readProcessStderrChan" exe args $ Left rv+ ExitSuccess ->+ return output+ where+ ignoreSEx = handle (\(SomeException _) -> return ())+ reader h = ignoreSEx $ do+ putErr . (++"\n") =<< hGetLine h+ reader h++withForkWait :: IO () -> (IO () -> IO a) -> IO a+withForkWait async body = do+ waitVar <- newEmptyMVar :: IO (MVar (Either SomeException ()))+ mask $ \restore -> do+ tid <- forkIO $ try (restore async) >>= putMVar waitVar+ let wait = takeMVar waitVar >>= either throwIO return+ restore (body wait) `onException` killThread tid
+ core/GhcMod/PathsAndFiles.hs view
@@ -0,0 +1,242 @@+-- ghc-mod: Happy Haskell Hacking+-- Copyright (C) 2015 Daniel Gröber <dxld ÄT darkboxed DOT org>+--+-- This program is free software: you can redistribute it and/or modify+-- it under the terms of the GNU Affero General Public License as published by+-- the Free Software Foundation, either version 3 of the License, or+-- (at your option) any later version.+--+-- This program is distributed in the hope that it will be useful,+-- but WITHOUT ANY WARRANTY; without even the implied warranty of+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+-- GNU Affero General Public License for more details.+--+-- You should have received a copy of the GNU Affero General Public License+-- along with this program. If not, see <http://www.gnu.org/licenses/>.++module GhcMod.PathsAndFiles (+ module GhcMod.PathsAndFiles+ , module GhcMod.Caching+ ) where++import Config (cProjectVersion)+import Control.Arrow (second)+import Control.Applicative+import Control.Exception as E+import Control.Monad+import Control.Monad.Trans.Maybe+import Data.List+import Data.Char+import Data.Maybe+import Data.Traversable hiding (mapM)+import Distribution.Helper (buildPlatform)+import System.Directory+import System.FilePath+import System.Process++import GhcMod.Types+import GhcMod.Caching+import qualified GhcMod.Utils as U+import Utils (mightExist)+import Prelude++-- | Guaranteed to be a path to a directory with no trailing slash.+type DirPath = FilePath++-- | Guaranteed to be the name of a file only (no slashes).+type FileName = String++-- | @findCabalFiles dir@. Searches for a @.cabal@ files in @dir@'s parent+-- directories. The first parent directory containing more than one cabal file+-- is assumed to be the project directory. If only one cabal file exists in this+-- directory it is returned otherwise @findCabalFiles@ throws 'GMENoCabalFile'+-- or 'GMETooManyCabalFiles'+findCabalFile :: FilePath -> IO (Maybe FilePath)+findCabalFile dir = findFileInParentsP isCabalFile pick dir+ where+ pick [] = Nothing+ pick [cf] = Just cf+ pick cfs = throw $ GMETooManyCabalFiles cfs++findStackConfigFile :: FilePath -> IO (Maybe FilePath)+findStackConfigFile dir =+ findFileInParentsP (=="stack.yaml") pick dir+ where+ pick [] = Nothing+ pick (sf:_) = Just sf++findCabalSandboxDir :: FilePath -> IO (Maybe FilePath)+findCabalSandboxDir dir =+ fmap takeDirectory <$> findFileInParentsP isSandboxConfig pick dir+ where+ isSandboxConfig = (==sandboxConfigFileName)+ pick [] = Nothing+ pick (sc:_) = Just sc++findCustomPackageDbFile :: FilePath -> IO (Maybe FilePath)+findCustomPackageDbFile dir =+ mightExist $ dir </> "ghc-mod.package-db-stack"++-- | Get path to sandbox config file+getSandboxDb :: Cradle -> IO (Maybe GhcPkgDb)+getSandboxDb crdl = do+ mConf <- traverse readFile =<< mightExist (sandboxConfigFile crdl)+ bp <- buildPlatform readProcess+ return $ PackageDb . fixPkgDbVer bp <$> (extractSandboxDbDir =<< mConf)++ where+ fixPkgDbVer bp dir =+ case takeFileName dir == ghcSandboxPkgDbDir bp of+ True -> dir+ False -> takeDirectory dir </> ghcSandboxPkgDbDir bp++-- | Extract the sandbox package db directory from the cabal.sandbox.config+-- file. Exception is thrown if the sandbox config file is broken.+extractSandboxDbDir :: String -> Maybe FilePath+extractSandboxDbDir conf = extractValue <$> parse conf+ where+ key = "package-db:"+ keyLen = length key++ parse = listToMaybe . filter (key `isPrefixOf`) . lines+ extractValue = U.dropWhileEnd isSpace . dropWhile isSpace . drop keyLen+++-- |+-- >>> isCabalFile "/home/user/.cabal"+-- False+isCabalFile :: FilePath -> Bool+isCabalFile f = takeExtension' f == ".cabal"++-- |+-- >>> takeExtension' "/some/dir/bla.cabal"+-- ".cabal"+--+-- >>> takeExtension' "some/reldir/bla.cabal"+-- ".cabal"+--+-- >>> takeExtension' "bla.cabal"+-- ".cabal"+--+-- >>> takeExtension' ".cabal"+-- ""+takeExtension' :: FilePath -> String+takeExtension' p =+ if takeFileName p == takeExtension p+ then "" -- just ".cabal" is not a valid cabal file+ else takeExtension p++-- | @findFileInParentsP p r dir@ Look for files satisfying @p@ in @dir@ and all+-- it's parent directories. Files found to satisfy @p@ in a given directory are+-- passed to @r@ and if this yields a 'Just' value the search finishes early+-- without examinig any more directories and this value is returned.+findFileInParentsP :: (FilePath -> Bool)+ -> ([FilePath] -> Maybe a)+ -> FilePath+ -> IO (Maybe a)+findFileInParentsP p r dir = runMaybeT $+ join $ msum <$> map (MaybeT . fmap r) <$> liftIO (findFilesInParentsP p dir)++-- | @findFilesInParentsP p dir@ Look for files satisfying @p@ in @dir@ and all+-- it's parent directories.+findFilesInParentsP :: (FilePath -> Bool) -> FilePath+ -> IO [IO [FilePath]]+findFilesInParentsP p dir' = U.makeAbsolute' dir' >>= \dir -> return $+ map (\d -> (map (d </>)) <$> getFilesP p d) $ parents dir++-- | @getFilesP p dir@. Find all __files__ satisfying @p@ in @.cabal@ in @dir@.+getFilesP :: (FilePath -> Bool) -> DirPath -> IO [FileName]+getFilesP p dir = filterM p' =<< getDirectoryContentsSafe+ where+ p' fn = do+ (p fn && ) <$> doesFileExist (dir </> fn)+ getDirectoryContentsSafe = do+ rdable <- readable <$> getPermissions dir+ if rdable+ then getDirectoryContents dir+ else return []++-- | @parents dir@. Returns all parent directories of @dir@ including @dir@.+--+-- Examples+--+-- >>> parents "foo"+-- ["foo"]+--+-- >>> parents "/foo"+-- ["/foo","/"]+--+-- >>> parents "/foo/bar"+-- ["/foo/bar","/foo","/"]+--+-- >>> parents "foo/bar"+-- ["foo/bar","foo"]+parents :: FilePath -> [FilePath]+parents "" = []+parents dir' =+ let (drive, dir) = splitDrive $ normalise $ dropTrailingPathSeparator dir'+ in map (joinDrive drive) $ parents' $ filter (/=".") $ splitDirectories dir+ where+ parents' :: [String] -> [FilePath]+ parents' [] | isAbsolute dir' = "":[]+ parents' [] = []+ parents' dir = [joinPath dir] ++ parents' (init dir)++----------------------------------------------------------------++setupConfigFile :: Cradle -> FilePath+setupConfigFile crdl =+ cradleRootDir crdl </> setupConfigPath (cradleDistDir crdl)++sandboxConfigFile :: Cradle -> FilePath+sandboxConfigFile crdl = cradleRootDir crdl </> sandboxConfigFileName++sandboxConfigFileName :: String+sandboxConfigFileName = "cabal.sandbox.config"++-- | Path to 'LocalBuildInfo' file, usually @dist/setup-config@+setupConfigPath :: FilePath -> FilePath+setupConfigPath dist = dist </> "setup-config"+ -- localBuildInfoFile defaultDistPref++macrosHeaderPath :: FilePath+macrosHeaderPath = autogenModulesDir </> "cabal_macros.h"++autogenModulePath :: String -> String+autogenModulePath pkg_name =+ autogenModulesDir </> ("Paths_" ++ map fixchar pkg_name) <.> ".hs"+ where fixchar '-' = '_'+ fixchar c = c++autogenModulesDir :: FilePath+autogenModulesDir = "build" </> "autogen"++ghcSandboxPkgDbDir :: String -> String+ghcSandboxPkgDbDir buildPlatf = do+ buildPlatf ++ "-ghc-" ++ cProjectVersion ++ "-packages.conf.d"++packageCache :: String+packageCache = "package.cache"++-- | Filename of the symbol table cache file.+symbolCache :: Cradle -> FilePath+symbolCache crdl = cradleRootDir crdl </> cradleDistDir crdl </> symbolCacheFile++symbolCacheFile :: String+symbolCacheFile = "ghc-mod.symbol-cache"++resolvedComponentsCacheFile :: FilePath -> FilePath+resolvedComponentsCacheFile dist =+ setupConfigPath dist <.> "ghc-mod.resolved-components"++cabalHelperCacheFile :: FilePath -> FilePath+cabalHelperCacheFile dist =+ setupConfigPath dist <.> "ghc-mod.cabal-components"++mergedPkgOptsCacheFile :: FilePath -> FilePath+mergedPkgOptsCacheFile dist =+ setupConfigPath dist <.> "ghc-mod.package-options"++pkgDbStackCacheFile :: FilePath -> FilePath+pkgDbStackCacheFile dist =+ setupConfigPath dist <.> "ghc-mod.package-db-stack"
+ core/GhcMod/Pretty.hs view
@@ -0,0 +1,90 @@+-- ghc-mod: Happy Haskell Hacking+-- Copyright (C) 2015 Daniel Gröber <dxld ÄT darkboxed DOT org>+--+-- This program is free software: you can redistribute it and/or modify+-- it under the terms of the GNU Affero General Public License as published by+-- the Free Software Foundation, either version 3 of the License, or+-- (at your option) any later version.+--+-- This program is distributed in the hope that it will be useful,+-- but WITHOUT ANY WARRANTY; without even the implied warranty of+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+-- GNU Affero General Public License for more details.+--+-- You should have received a copy of the GNU Affero General Public License+-- along with this program. If not, see <http://www.gnu.org/licenses/>.++module GhcMod.Pretty+ ( renderGm+ , renderSDoc+ , gmComponentNameDoc+ , gmLogLevelDoc+ , (<+>:)+ , fnDoc+ , showToDoc+ , warnDoc+ , strLnDoc+ , strDoc+ ) where++import Control.Arrow hiding ((<+>))+import Data.Char+import Data.List+import Distribution.Helper+import Pretty+import GHC+import Outputable (SDoc, withPprStyleDoc)++import GhcMod.Types+import GhcMod.Doc+import GhcMod.Gap (renderGm)++renderSDoc :: GhcMonad m => SDoc -> m Doc+renderSDoc sdoc = do+ df <- getSessionDynFlags+ ppsty <- getStyle+ return $ withPprStyleDoc df ppsty sdoc++gmComponentNameDoc :: ChComponentName -> Doc+gmComponentNameDoc ChSetupHsName = text $ "Setup.hs"+gmComponentNameDoc (ChLibName "") = text $ "library"+gmComponentNameDoc (ChLibName n) = text $ "library:" ++ n+gmComponentNameDoc (ChExeName n) = text $ "exe:" ++ n+gmComponentNameDoc (ChTestName n) = text $ "test:" ++ n+gmComponentNameDoc (ChBenchName n) = text $ "bench:" ++ n++gmLogLevelDoc :: GmLogLevel -> Doc+gmLogLevelDoc GmSilent = error "GmSilent MUST not be used for log messages"+gmLogLevelDoc GmPanic = text "PANIC"+gmLogLevelDoc GmException = text "EXCEPTION"+gmLogLevelDoc GmError = text "ERROR"+gmLogLevelDoc GmWarning = text "Warning"+gmLogLevelDoc GmInfo = text "info"+gmLogLevelDoc GmDebug = text "DEBUG"+gmLogLevelDoc GmVomit = text "VOMIT"++infixl 6 <+>:+(<+>:) :: Doc -> Doc -> Doc+a <+>: b = (a <> colon) <+> b++fnDoc :: FilePath -> Doc+fnDoc = doubleQuotes . text++showToDoc :: Show a => a -> Doc+showToDoc = strLnDoc . show++warnDoc :: Doc -> Doc+warnDoc d = text "Warning" <+>: d++strLnDoc :: String -> Doc+strLnDoc str = doc (dropWhileEnd isSpace str)+ where+ doc = lines >>> map text >>> foldr ($+$) empty++strDoc :: String -> Doc+strDoc str = doc (dropWhileEnd isSpace str)+ where+ doc :: String -> Doc+ doc = lines+ >>> map (words >>> map text >>> fsep)+ >>> \l -> case l of (x:xs) -> hang x 4 (vcat xs); [] -> empty
+ core/GhcMod/Read.hs view
@@ -0,0 +1,106 @@+module GhcMod.Read where++import Text.Read (readPrec_to_S, readPrec, minPrec)+import qualified Text.ParserCombinators.ReadP as P+import Text.ParserCombinators.ReadPrec (lift)++-- This library (libraries/base) is derived from code from several+-- sources:++-- * Code from the GHC project which is largely (c) The University of+-- Glasgow, and distributable under a BSD-style license (see below),++-- * Code from the Haskell 98 Report which is (c) Simon Peyton Jones+-- and freely redistributable (but see the full license for+-- restrictions).++-- * Code from the Haskell Foreign Function Interface specification,+-- which is (c) Manuel M. T. Chakravarty and freely redistributable+-- (but see the full license for restrictions).++-- The full text of these licenses is reproduced below. All of the+-- licenses are BSD-style or compatible.++-- -----------------------------------------------------------------------------++-- The Glasgow Haskell Compiler License++-- Copyright 2004, The University Court of the University of Glasgow.+-- All rights reserved.++-- Redistribution and use in source and binary forms, with or without+-- modification, are permitted provided that the following conditions are met:++-- - Redistributions of source code must retain the above copyright notice,+-- this list of conditions and the following disclaimer.++-- - Redistributions in binary form must reproduce the above copyright notice,+-- this list of conditions and the following disclaimer in the documentation+-- and/or other materials provided with the distribution.++-- - Neither name of the University nor the names of its contributors may be+-- used to endorse or promote products derived from this software without+-- specific prior written permission.++-- THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY COURT OF THE UNIVERSITY OF+-- GLASGOW AND THE CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,+-- INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND+-- FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE+-- UNIVERSITY COURT OF THE UNIVERSITY OF GLASGOW OR THE CONTRIBUTORS BE LIABLE+-- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL+-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR+-- SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER+-- CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT+-- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY+-- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH+-- DAMAGE.++-- -----------------------------------------------------------------------------++-- Code derived from the document "Report on the Programming Language+-- Haskell 98", is distributed under the following license:++-- Copyright (c) 2002 Simon Peyton Jones++-- The authors intend this Report to belong to the entire Haskell+-- community, and so we grant permission to copy and distribute it for+-- any purpose, provided that it is reproduced in its entirety,+-- including this Notice. Modified versions of this Report may also be+-- copied and distributed for any purpose, provided that the modified+-- version is clearly presented as such, and that it does not claim to+-- be a definition of the Haskell 98 Language.++-- -----------------------------------------------------------------------------++-- Code derived from the document "The Haskell 98 Foreign Function+-- Interface, An Addendum to the Haskell 98 Report" is distributed under+-- the following license:++-- Copyright (c) 2002 Manuel M. T. Chakravarty++-- The authors intend this Report to belong to the entire Haskell+-- community, and so we grant permission to copy and distribute it for+-- any purpose, provided that it is reproduced in its entirety,+-- including this Notice. Modified versions of this Report may also be+-- copied and distributed for any purpose, provided that the modified+-- version is clearly presented as such, and that it does not claim to+-- be a definition of the Haskell 98 Foreign Function Interface.++-- -----------------------------------------------------------------------------++readEither :: Read a => String -> Either String a+readEither s =+ case [ x | (x,"") <- readPrec_to_S read' minPrec s ] of+ [x] -> Right x+ [] -> Left "Prelude.read: no parse"+ _ -> Left "Prelude.read: ambiguous parse"+ where+ read' =+ do x <- readPrec+ lift P.skipSpaces+ return x++readMaybe :: Read a => String -> Maybe a+readMaybe s = case readEither s of+ Left _ -> Nothing+ Right a -> Just a
+ core/GhcMod/SrcUtils.hs view
@@ -0,0 +1,191 @@+-- TODO: remove CPP once Gap(ed)+{-# LANGUAGE CPP, TupleSections, FlexibleInstances, Rank2Types #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}++module GhcMod.SrcUtils where++import Control.Applicative+import CoreUtils (exprType)+import Data.Generics+import Data.Maybe+import Data.Ord as O+import GHC (LHsExpr, LPat, Id, DynFlags, SrcSpan, Type, Located, ParsedSource, RenamedSource, TypecheckedSource, GenLocated(L))+import Var (Var)+import qualified GHC as G+import qualified Var as G+import qualified Type as G+import GHC.SYB.Utils+import GhcMonad+import qualified Language.Haskell.Exts as HE+import GhcMod.Doc+import GhcMod.Gap+import qualified GhcMod.Gap as Gap+import OccName (OccName)+import Outputable (PprStyle)+import TcHsSyn (hsPatType)+import Prelude+import Control.Monad+import Data.List (nub)+import Control.Arrow+import qualified Data.Map as M++----------------------------------------------------------------++instance HasType (LHsExpr Id) where+ getType tcm e = do+ hs_env <- G.getSession+ mbe <- liftIO $ Gap.deSugar tcm e hs_env+ return $ (G.getLoc e, ) <$> CoreUtils.exprType <$> mbe++instance HasType (LPat Id) where+ getType _ (G.L spn pat) = return $ Just (spn, hsPatType pat)++----------------------------------------------------------------++-- | Stores mapping from monomorphic to polymorphic types+type CstGenQS = M.Map Var Type+-- | Generic type to simplify SYB definition+type CstGenQT a = forall m. GhcMonad m => a Id -> CstGenQS -> (m [(SrcSpan, Type)], CstGenQS)++collectSpansTypes :: (GhcMonad m) => Bool -> G.TypecheckedModule -> (Int, Int) -> m [(SrcSpan, Type)]+collectSpansTypes withConstraints tcs lc =+ -- This walks AST top-down, left-to-right, while carrying CstGenQS down the tree+ -- (but not left-to-right)+ everythingStagedWithContext TypeChecker M.empty (liftM2 (++))+ (return [])+ ((return [],)+ `mkQ` (hsBind :: CstGenQT G.LHsBind) -- matches on binds+ `extQ` (genericCT :: CstGenQT G.LHsExpr) -- matches on expressions+ `extQ` (genericCT :: CstGenQT G.LPat) -- matches on patterns+ )+ (G.tm_typechecked_source tcs)+ where+ -- Helper function to insert mapping into CstGenQS+ insExp x = M.insert (G.abe_mono x) (G.varType $ G.abe_poly x)+ -- If there is AbsBinds here, insert mapping into CstGenQS if needed+ hsBind (L _ G.AbsBinds{abs_exports = es'}) s+ | withConstraints = (return [], foldr insExp s es')+ | otherwise = (return [], s)+#if __GLASGOW_HASKELL__ >= 800+ -- TODO: move to Gap+ -- Note: this deals with bindings with explicit type signature, e.g.+ -- double :: Num a => a -> a+ -- double x = 2*x+ hsBind (L _ G.AbsBindsSig{abs_sig_export = poly, abs_sig_bind = bind}) s+ | withConstraints =+ let new_s =+ case bind of+ G.L _ G.FunBind{fun_id = i} -> M.insert (G.unLoc i) (G.varType poly) s+ _ -> s+ in (return [], new_s)+ | otherwise = (return [], s)+#endif+ -- Otherwise, it's the same as other cases+ hsBind x s = genericCT x s+ -- Generic SYB function to get type+ genericCT x s+ | withConstraints+ = (maybe [] (uncurry $ constrainedType (collectBinders x) s) <$> getType' x, s)+ | otherwise = (maybeToList <$> getType' x, s)+ -- Collects everything with Id from LHsBind, LHsExpr, or LPat+ collectBinders :: Data a => a -> [Id]+ collectBinders = listifyStaged TypeChecker (const True)+ -- Gets monomorphic type with location+ getType' x@(L spn _)+ | G.isGoodSrcSpan spn && spn `G.spans` lc+ = getType tcs x+ | otherwise = return Nothing+ -- Gets constrained type+ constrainedType :: [Var] -- ^ Binders in expression, i.e. anything with Id+ -> CstGenQS -- ^ Map from Id to polymorphic type+ -> SrcSpan -- ^ extent of expression, copied to result+ -> Type -- ^ monomorphic type+ -> [(SrcSpan, Type)] -- ^ result+ constrainedType pids s spn genTyp =+ let+ -- runs build on every binder.+ ctys = mapMaybe build (nub pids)+ -- Computes constrained type for x. Returns (constraints, substitutions)+ -- Substitutions are needed because type variables don't match+ -- between polymorphic and monomorphic types.+ -- E.g. poly type might be `Monad m => m ()`, while monomorphic might be `f ()`+ build x | Just cti <- x `M.lookup` s+ = let+ (preds', ctt) = getPreds cti+ -- list of type variables in monomorphic type+ vts = listifyStaged TypeChecker G.isTyVar $ G.varType x+ -- list of type variables in polymorphic type+ tvm = listifyStaged TypeChecker G.isTyVarTy ctt+ in Just (preds', zip vts tvm)+ | otherwise = Nothing+ -- list of constraints+ preds = concatMap fst ctys+ -- Type variable substitutions+#if __GLASGOW_HASKELL__ >= 800+ -- TODO: move to Gap+ subs = G.mkTvSubstPrs $ concatMap snd ctys+#else+ subs = G.mkTopTvSubst $ concatMap snd ctys+#endif+ -- Constrained type+ ty = G.substTy subs $ G.mkFunTys preds genTyp+ in [(spn, ty)]+ -- Splits a given type into list of constraints and simple type. Drops foralls.+ getPreds :: Type -> ([Type], Type)+ getPreds x | G.isForAllTy x = getPreds $ G.dropForAlls x+ | Just (c, t) <- G.splitFunTy_maybe x+ , G.isPredTy c = first (c:) $ getPreds t+ | otherwise = ([], x)++listifySpans :: Typeable a => TypecheckedSource -> (Int, Int) -> [Located a]+listifySpans tcs lc = listifyStaged TypeChecker p tcs+ where+ p (L spn _) = G.isGoodSrcSpan spn && spn `G.spans` lc++listifyParsedSpans :: Typeable a => ParsedSource -> (Int, Int) -> [Located a]+listifyParsedSpans pcs lc = listifyStaged Parser p pcs+ where+ p (L spn _) = G.isGoodSrcSpan spn && spn `G.spans` lc++listifyRenamedSpans :: Typeable a => RenamedSource -> (Int, Int) -> [Located a]+listifyRenamedSpans pcs lc = listifyStaged Renamer p pcs+ where+ p (L spn _) = G.isGoodSrcSpan spn && spn `G.spans` lc++listifyStaged :: Typeable r => Stage -> (r -> Bool) -> GenericQ [r]+listifyStaged s p = everythingStaged s (++) [] ([] `mkQ` (\x -> [x | p x]))++cmp :: SrcSpan -> SrcSpan -> Ordering+cmp a b+ | a `G.isSubspanOf` b = O.LT+ | b `G.isSubspanOf` a = O.GT+ | otherwise = O.EQ++toTup :: DynFlags -> PprStyle -> (SrcSpan, Type) -> ((Int,Int,Int,Int),String)+toTup dflag style (spn, typ) = (fourInts spn, pretty dflag style typ)++fourInts :: SrcSpan -> (Int,Int,Int,Int)+fourInts = fromMaybe (0,0,0,0) . Gap.getSrcSpan++fourIntsHE :: HE.SrcSpan -> (Int,Int,Int,Int)+fourIntsHE loc = ( HE.srcSpanStartLine loc, HE.srcSpanStartColumn loc+ , HE.srcSpanEndLine loc, HE.srcSpanEndColumn loc)++-- Check whether (line,col) is inside a given SrcSpanInfo+typeSigInRangeHE :: Int -> Int -> HE.Decl HE.SrcSpanInfo -> Bool+typeSigInRangeHE lineNo colNo (HE.TypeSig (HE.SrcSpanInfo s _) _ _) =+ HE.srcSpanStart s <= (lineNo, colNo) && HE.srcSpanEnd s >= (lineNo, colNo)+typeSigInRangeHE lineNo colNo (HE.TypeFamDecl (HE.SrcSpanInfo s _) _ _ _) =+ HE.srcSpanStart s <= (lineNo, colNo) && HE.srcSpanEnd s >= (lineNo, colNo)+typeSigInRangeHE lineNo colNo (HE.DataFamDecl (HE.SrcSpanInfo s _) _ _ _) =+ HE.srcSpanStart s <= (lineNo, colNo) && HE.srcSpanEnd s >= (lineNo, colNo)+typeSigInRangeHE _ _ _= False++pretty :: DynFlags -> PprStyle -> Type -> String+pretty dflag style = showOneLine dflag style . Gap.typeForUser++showName :: DynFlags -> PprStyle -> G.Name -> String+showName dflag style name = showOneLine dflag style $ Gap.nameForUser name++showOccName :: DynFlags -> PprStyle -> OccName -> String+showOccName dflag style name = showOneLine dflag style $ Gap.occNameForUser name
+ core/GhcMod/Stack.hs view
@@ -0,0 +1,97 @@+-- ghc-mod: Happy Haskell Hacking+-- Copyright (C) 2015 Daniel Gröber <dxld ÄT darkboxed DOT org>+--+-- This program is free software: you can redistribute it and/or modify+-- it under the terms of the GNU Affero General Public License as published by+-- the Free Software Foundation, either version 3 of the License, or+-- (at your option) any later version.+--+-- This program is distributed in the hope that it will be useful,+-- but WITHOUT ANY WARRANTY; without even the implied warranty of+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+-- GNU Affero General Public License for more details.+--+-- You should have received a copy of the GNU Affero General Public License+-- along with this program. If not, see <http://www.gnu.org/licenses/>.++module GhcMod.Stack where++import Safe+import Control.Applicative+import Control.Exception as E+import Control.Monad+import Control.Monad.Trans.Maybe+import Control.Monad.Trans.Class+import Data.List+import Data.List.Split+import Data.Maybe+import System.Directory+import System.FilePath+import System.Info.Extra+import Exception++import GhcMod.Types+import GhcMod.Monad.Types+import GhcMod.Output+import GhcMod.Logging+import GhcMod.Error+import qualified GhcMod.Utils as U+import Prelude++patchStackPrograms :: (IOish m, GmOut m) => Cradle -> Programs -> m Programs+patchStackPrograms Cradle { cradleProject = (StackProject senv) } progs = do+ Just ghc <- getStackGhcPath senv+ Just ghcPkg <- getStackGhcPkgPath senv+ return $ progs {+ ghcProgram = ghc+ , ghcPkgProgram = ghcPkg+ }+patchStackPrograms _crdl progs = return progs++getStackEnv :: (IOish m, GmOut m, GmLog m)+ => FilePath -> FilePath -> m (Maybe StackEnv)+getStackEnv projdir stackProg = U.withDirectory_ projdir $ runMaybeT $ do+ env <- map (liToTup . splitOn ": ") . lines <$> readStack stackProg ["path"]+ let look k = fromJustNote "getStackEnv" $ lookup k env+ return StackEnv {+ seDistDir = look "dist-dir"+ , seBinPath = splitSearchPath $ look "bin-path"+ , seSnapshotPkgDb = look "snapshot-pkg-db"+ , seLocalPkgDb = look "local-pkg-db"+ }+ where+ liToTup [k,v] = (k,v)+ liToTup [k] = (k, error "getStackEnv: missing key '"++k++"'")+ liToTup _ = error "getStackEnv"++getStackGhcPath :: IOish m => StackEnv -> m (Maybe FilePath)+getStackGhcPath = findExecutablesInStackBinPath "ghc"++getStackGhcPkgPath :: IOish m => StackEnv -> m (Maybe FilePath)+getStackGhcPkgPath = findExecutablesInStackBinPath "ghc-pkg"++findExecutablesInStackBinPath :: IOish m => String -> StackEnv -> m (Maybe FilePath)+findExecutablesInStackBinPath exe StackEnv {..} =+ liftIO $ listToMaybe <$> findExecutablesInDirectories' seBinPath exe++findExecutablesInDirectories' :: [FilePath] -> String -> IO [FilePath]+findExecutablesInDirectories' path binary =+ U.findFilesWith' isExecutable path (binary <.> exeExtension')+ where isExecutable file = do+ perms <- getPermissions file+ return $ executable perms++ exeExtension' = if isWindows then "exe" else ""++readStack :: (IOish m, GmOut m, GmLog m)+ => FilePath -> [String] -> MaybeT m String+readStack exe args = do+ stack <- MaybeT $ liftIO $ findExecutable exe+ readProc <- lift gmReadProcess+ flip gcatch handler $ do+ liftIO $ evaluate =<< readProc stack args ""+ where+ handler (e :: IOError) = do+ gmLog GmWarning "readStack" $ gmeDoc $ exToErr e+ mzero+ exToErr = GMEStackBootstrap . GMEString . show
+ core/GhcMod/Target.hs view
@@ -0,0 +1,515 @@+-- ghc-mod: Happy Haskell Hacking+-- Copyright (C) 2015 Daniel Gröber <dxld ÄT darkboxed DOT org>+--+-- This program is free software: you can redistribute it and/or modify+-- it under the terms of the GNU Affero General Public License as published by+-- the Free Software Foundation, either version 3 of the License, or+-- (at your option) any later version.+--+-- This program is distributed in the hope that it will be useful,+-- but WITHOUT ANY WARRANTY; without even the implied warranty of+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+-- GNU Affero General Public License for more details.+--+-- You should have received a copy of the GNU Affero General Public License+-- along with this program. If not, see <http://www.gnu.org/licenses/>.++{-# LANGUAGE CPP, ViewPatterns, NamedFieldPuns, RankNTypes #-}+module GhcMod.Target where++import Control.Arrow+import Control.Applicative+import Control.Category ((.))+import GHC+#if __GLASGOW_HASKELL__ >= 800+import GHC.LanguageExtensions+#endif+import GHC.Paths (libdir)+import SysTools+import DynFlags+import HscTypes+import Pretty++import GhcMod.DynFlags+import GhcMod.Monad.Types+import GhcMod.CabalHelper+import GhcMod.HomeModuleGraph+import GhcMod.PathsAndFiles+import GhcMod.GhcPkg+import GhcMod.Error+import GhcMod.Logging+import GhcMod.Types+import GhcMod.Utils as U+import GhcMod.FileMapping+import GhcMod.LightGhc+import GhcMod.CustomPackageDb+import GhcMod.Output++import Safe+import Data.Maybe+import Data.Monoid as Monoid+import Data.Either+import Data.Foldable as Foldable (foldrM)+import qualified Data.Foldable as Foldable+import Data.Traversable hiding (mapM, forM)+import Data.IORef+import Data.List+import Data.Map (Map)+import qualified Data.Map as Map+import Data.Set (Set)+import qualified Data.Set as Set+import Data.Function (on)+import Distribution.Helper+import Prelude hiding ((.))++import System.Directory+import System.FilePath++runGmPkgGhc :: (IOish m, Gm m) => LightGhc a -> m a+runGmPkgGhc action = do+ pkgOpts <- packageGhcOptions+ withLightHscEnv pkgOpts $ \env -> liftIO $ runLightGhc env action++initSession :: IOish m+ => [GHCOption]+ -> (forall gm. GhcMonad gm => DynFlags -> gm DynFlags)+ -> GhcModT m ()+initSession opts mdf = do+ s <- gmsGet+ case gmGhcSession s of+ Nothing -> do+ gmLog GmDebug "initSession" $ text "Session not initialized, creating new one"+ putNewSession s+ Just (GmGhcSession hsc_env_ref) -> do+ crdl <- cradle++ df <- liftIO $ hsc_dflags <$> readIORef hsc_env_ref+ changed <-+ withLightHscEnv' (initDF crdl) $ \hsc_env ->+ return $ not $ hsc_dflags hsc_env `eqDynFlags` df++ if changed+ then do+ gmLog GmDebug "initSession" $ text "Flags changed, creating new session"+ teardownSession hsc_env_ref+ putNewSession s+ else+ gmLog GmDebug "initSession" $ text "Session already initialized"+ where+ initDF Cradle { cradleTempDir } df =+ setTmpDir cradleTempDir <$> (mdf =<< addCmdOpts opts df)++ teardownSession hsc_env_ref = do+ hsc_env <- liftIO $ readIORef hsc_env_ref+ teardownLightEnv hsc_env++ putNewSession :: IOish m => GhcModState -> GhcModT m ()+ putNewSession s = do+ crdl <- cradle+ nhsc_env_ref <- liftIO . newIORef =<< newLightEnv (initDF crdl)+ runLightGhc' nhsc_env_ref $ setSessionDynFlags =<< getSessionDynFlags+ gmsPut s { gmGhcSession = Just $ GmGhcSession nhsc_env_ref }+++-- | Drop the currently active GHC session, the next that requires a GHC session+-- will initialize a new one.+dropSession :: IOish m => GhcModT m ()+dropSession = do+ s <- gmsGet+ case gmGhcSession s of+ Just (GmGhcSession ref) -> do+ -- TODO: This is still not enough, there seem to still be references to+ -- GHC's state around afterwards.+ liftIO $ writeIORef ref (error "HscEnv: session was dropped")+ -- Not available on ghc<7.8; didn't really help anyways+ -- liftIO $ setUnsafeGlobalDynFlags (error "DynFlags: session was dropped")+ gmsPut s { gmGhcSession = Nothing }++ Nothing -> return ()++-- | Run a GmlT action (i.e. a function in the GhcMonad) in the context+-- of certain files or modules+runGmlT :: IOish m => [Either FilePath ModuleName] -> GmlT m a -> GhcModT m a+runGmlT fns action = runGmlT' fns return action++-- | Run a GmlT action (i.e. a function in the GhcMonad) in the context+-- of certain files or modules, with updated GHC flags+runGmlT' :: IOish m+ => [Either FilePath ModuleName]+ -> (forall gm. GhcMonad gm => DynFlags -> gm DynFlags)+ -> GmlT m a+ -> GhcModT m a+runGmlT' fns mdf action = runGmlTWith fns mdf id action++-- | Run a GmlT action (i.e. a function in the GhcMonad) in the context+-- of certain files or modules, with updated GHC flags and a final+-- transformation+runGmlTWith :: IOish m+ => [Either FilePath ModuleName]+ -> (forall gm. GhcMonad gm => DynFlags -> gm DynFlags)+ -> (GmlT m a -> GmlT m b)+ -> GmlT m a+ -> GhcModT m b+runGmlTWith efnmns' mdf wrapper action = do+ crdl <- cradle+ Options { optGhcUserOptions } <- options++ let (fns, mns) = partitionEithers efnmns'+ ccfns = map (cradleCurrentDir crdl </>) fns+ cfns <- mapM getCanonicalFileNameSafe ccfns+ let serfnmn = Set.fromList $ map Right mns ++ map Left cfns+ opts <- targetGhcOptions crdl serfnmn+ let opts' = opts ++ ["-O0"] ++ optGhcUserOptions++ gmVomit+ "session-ghc-options"+ (text "Initializing GHC session with following options")+ (intercalate " " $ map (("\""++) . (++"\"")) opts')++ GhcModLog { gmLogLevel = Just level } <- gmlHistory+ putErr <- gmErrStrIO+ let setLogger | level >= GmDebug = setDebugLogger putErr+ | otherwise = setEmptyLogger++ initSession opts' $+ setHscNothing >>> setLogger >>> mdf++ mappedStrs <- getMMappedFilePaths+ let targetStrs = mappedStrs ++ map moduleNameString mns ++ cfns++ unGmlT $ wrapper $ do+ loadTargets opts targetStrs+ action++targetGhcOptions :: forall m. IOish m+ => Cradle+ -> Set (Either FilePath ModuleName)+ -> GhcModT m [GHCOption]+targetGhcOptions crdl sefnmn = do+ when (Set.null sefnmn) $ error "targetGhcOptions: no targets given"++ case cradleProject crdl of+ proj+ | isCabalHelperProject proj -> cabalOpts crdl+ | otherwise -> sandboxOpts crdl+ where+ zipMap f l = l `zip` (f `map` l)++ cabalOpts :: Cradle -> GhcModT m [String]+ cabalOpts Cradle{..} = do+ mcs <- cabalResolvedComponents++ let mdlcs = moduleComponents mcs `zipMap` Set.toList sefnmn+ candidates = findCandidates $ map snd mdlcs++ let noCandidates = Set.null candidates+ noModuleHasAnyAssignment = all (Set.null . snd) mdlcs++ if noCandidates && noModuleHasAnyAssignment+ then do+ -- First component should be ChLibName, if no lib will take lexically first exe.+ let cns = filter (/= ChSetupHsName) $ Map.keys mcs++ gmLog GmDebug "" $ strDoc $ "Could not find a component assignment, falling back to picking library component in cabal file."+ return $ gmcGhcOpts $ fromJustNote "targetGhcOptions, no-assignment" $ Map.lookup (head cns) mcs+ else do+ when noCandidates $+ throwError $ GMECabalCompAssignment mdlcs++ let cn = pickComponent candidates+ return $ gmcGhcOpts $ fromJustNote "targetGhcOptions" $ Map.lookup cn mcs++resolvedComponentsCache :: IOish m => FilePath ->+ Cached (GhcModT m) GhcModState+ [GmComponent 'GMCRaw (Set.Set ModulePath)]+ (Map.Map ChComponentName (GmComponent 'GMCResolved (Set.Set ModulePath)))+resolvedComponentsCache distdir = Cached {+ cacheLens = Just (lGmcResolvedComponents . lGmCaches),+ cacheFile = resolvedComponentsCacheFile distdir,+ cachedAction = \tcfs comps ma -> do+ Cradle {..} <- cradle+ let iifs = invalidatingInputFiles tcfs++ setupChanged =+ (cradleRootDir </> setupConfigPath distdir) `elem` iifs++ mums :: Maybe [Either FilePath ModuleName]+ mums =+ let+ filterOutSetupCfg =+ filter (/= cradleRootDir </> setupConfigPath distdir)+ changedFiles = filterOutSetupCfg iifs+ in if null changedFiles || setupChanged+ then Nothing+ else Just $ map Left changedFiles++ let mdesc (Left f) = "file:" ++ f+ mdesc (Right mn) = "module:" ++ moduleNameString mn++ changed = map (text . mdesc) $ Foldable.concat mums+ changedDoc | [] <- changed = text "none"+ | otherwise = sep changed++ gmLog GmDebug "resolvedComponentsCache" $+ text "files changed" <+>: changedDoc++ mcs <- resolveGmComponents ((,) <$> mums <*> ma) comps++ return (setupConfigPath distdir : flatten mcs , mcs)+ }++ where+ flatten :: Map.Map ChComponentName (GmComponent t (Set.Set ModulePath))+ -> [FilePath]+ flatten = Map.elems+ >>> map (gmcHomeModuleGraph >>> gmgGraph+ >>> (Map.keysSet &&& Map.elems)+ >>> uncurry insert+ >>> map (Set.map mpPath)+ >>> Set.unions+ )+ >>> Set.unions+ >>> Set.toList++moduleComponents :: Map ChComponentName (GmComponent t (Set ModulePath))+ -> Either FilePath ModuleName+ -> Set ChComponentName+moduleComponents m efnmn =+ foldr' Set.empty m $ \c s ->+ let+ memb =+ case efnmn of+ Left fn -> fn `Set.member` Set.map mpPath (smp c)+ Right mn -> mn `Set.member` Set.map mpModule (smp c)+ in if memb+ then Set.insert (gmcName c) s+ else s+ where+ smp c = Map.keysSet $ gmgGraph $ gmcHomeModuleGraph c++ foldr' b as f = Map.foldr f b as+++findCandidates :: [Set ChComponentName] -> Set ChComponentName+findCandidates [] = Set.empty+findCandidates scns = foldl1 Set.intersection scns++pickComponent :: Set ChComponentName -> ChComponentName+pickComponent scn = Set.findMin scn++packageGhcOptions :: (IOish m, Applicative m, Gm m) => m [GHCOption]+packageGhcOptions = do+ crdl <- cradle+ case cradleProject crdl of+ proj+ | isCabalHelperProject proj -> getGhcMergedPkgOptions+ | otherwise -> sandboxOpts crdl++-- also works for plain projects!+sandboxOpts :: (IOish m, GmEnv m) => Cradle -> m [String]+sandboxOpts crdl = do+ mCusPkgDb <- getCustomPkgDbStack+ pkgDbStack <- liftIO $ getSandboxPackageDbStack+ let pkgOpts = ghcDbStackOpts $ fromMaybe pkgDbStack mCusPkgDb+ return $ ["-i" ++ d | d <- [wdir,rdir]] ++ pkgOpts ++ ["-Wall"]+ where+ (wdir, rdir) = (cradleCurrentDir crdl, cradleRootDir crdl)++ getSandboxPackageDbStack :: IO [GhcPkgDb]+ getSandboxPackageDbStack =+ ([GlobalDb] ++) . maybe [UserDb] return <$> getSandboxDb crdl++resolveGmComponent :: (IOish m, Gm m)+ => Maybe [CompilationUnit] -- ^ Updated modules+ -> GmComponent 'GMCRaw (Set ModulePath)+ -> m (GmComponent 'GMCResolved (Set ModulePath))+resolveGmComponent mums c@GmComponent {..} = do+ distDir <- cradleDistDir <$> cradle+ gmLog GmDebug "resolveGmComponent" $ text $ show $ ghcOpts distDir+ withLightHscEnv (ghcOpts distDir) $ \env -> do+ let srcDirs = if null gmcSourceDirs then [""] else gmcSourceDirs+ let mg = gmcHomeModuleGraph+ let simp = gmcEntrypoints+ sump <- case mums of+ Nothing -> return simp+ Just ums ->+ Set.fromList . catMaybes <$>+ mapM (resolveModule env srcDirs) ums++ mg' <- canonicalizeModuleGraph =<< updateHomeModuleGraph env mg simp sump++ return $ c { gmcEntrypoints = simp, gmcHomeModuleGraph = mg' }++ where ghcOpts distDir = concat [+ gmcGhcSrcOpts,+ gmcGhcLangOpts,+ [ "-optP-include", "-optP" ++ distDir </> macrosHeaderPath ]+ ]++resolveEntrypoint :: (IOish m, Gm m)+ => Cradle+ -> GmComponent 'GMCRaw ChEntrypoint+ -> m (GmComponent 'GMCRaw (Set ModulePath))+resolveEntrypoint Cradle {..} c@GmComponent {..} = do+ gmLog GmDebug "resolveEntrypoint" $ text $ show $ gmcGhcSrcOpts+ withLightHscEnv gmcGhcSrcOpts $ \env -> do+ let srcDirs = if null gmcSourceDirs then [""] else gmcSourceDirs+ eps <- liftIO $ resolveChEntrypoints cradleRootDir gmcEntrypoints+ rms <- resolveModule env srcDirs `mapM` eps+ return c { gmcEntrypoints = Set.fromList $ catMaybes rms }++-- TODO: remember that the file from `main-is:` is always module `Main` and let+-- ghc do the warning about it. Right now we run that module through+-- resolveModule like any other+resolveChEntrypoints :: FilePath -> ChEntrypoint -> IO [CompilationUnit]+resolveChEntrypoints _ (ChLibEntrypoint em om) =+ return $ map (Right . chModToMod) (em ++ om)++resolveChEntrypoints _ (ChExeEntrypoint main om) =+ return $ [Left main] ++ map (Right . chModToMod) om++resolveChEntrypoints srcDir ChSetupEntrypoint = do+ shs <- doesFileExist (srcDir </> "Setup.hs")+ slhs <- doesFileExist (srcDir </> "Setup.lhs")+ return $ case (shs, slhs) of+ (True, _) -> [Left "Setup.hs"]+ (_, True) -> [Left "Setup.lhs"]+ (False, False) -> []++chModToMod :: ChModuleName -> ModuleName+chModToMod (ChModuleName mn) = mkModuleName mn+++resolveModule :: (IOish m, Gm m) =>+ HscEnv -> [FilePath] -> CompilationUnit -> m (Maybe ModulePath)+resolveModule env _srcDirs (Right mn) =+ liftIO $ traverse canonicalizeModulePath =<< findModulePath env mn+resolveModule env srcDirs (Left fn') = do+ mfn <- liftIO $ findFile' srcDirs fn'+ case mfn of+ Nothing -> return Nothing+ Just fn'' -> do+ fn <- liftIO $ canonicalizePath fn''+ emn <- fileModuleName env fn+ case emn of+ Left errs -> do+ gmLog GmWarning ("resolveModule " ++ show fn) $+ Pretty.empty $+$ (vcat $ map text errs)+ return Nothing -- TODO: should expose these errors otherwise+ -- modules with preprocessor/parse errors are+ -- going to be missing+ Right mmn -> return $ Just $+ case mmn of+ Nothing -> mkMainModulePath fn+ Just mn -> ModulePath mn fn+ where+ -- needed for ghc 7.4+ findFile' dirs file =+ getFirst . mconcat <$> mapM (fmap First . mightExist . (</>file)) dirs++ -- fileModuleName fn (dir:dirs)+ -- | makeRelative dir fn /= fn++type CompilationUnit = Either FilePath ModuleName+type Components =+ [GmComponent 'GMCRaw (Set ModulePath)]+type ResolvedComponentsMap =+ Map ChComponentName (GmComponent 'GMCResolved (Set ModulePath))++resolveGmComponents :: (IOish m, Gm m)+ => Maybe ([CompilationUnit], ResolvedComponentsMap)+ -- ^ Updated modules+ -> Components -> m ResolvedComponentsMap+resolveGmComponents mcache cs = do+ let rcm = fromMaybe Map.empty $ snd <$> mcache++ m' <- foldrM' rcm cs $ \c m -> do+ case Map.lookup (gmcName c) m of+ Nothing -> insertUpdated m c+ Just c' -> if same gmcRawEntrypoints c c' && same gmcGhcSrcOpts c c'+ then return m+ else insertUpdated m c+ return m'++ where+ foldrM' b fa f = foldrM f b fa+ insertUpdated m c = do+ rc <- resolveGmComponent (fst <$> mcache) c+ return $ Map.insert (gmcName rc) rc m++ same :: Eq b+ => (forall t a. GmComponent t a -> b)+ -> GmComponent u c -> GmComponent v d -> Bool+ same f a b = (f a) == (f b)++-- | Set the files as targets and load them.+loadTargets :: IOish m => [GHCOption] -> [FilePath] -> GmlT m ()+loadTargets opts targetStrs = do+ targets' <-+ withLightHscEnv opts $ \env ->+ liftM (nubBy ((==) `on` targetId))+ (mapM ((`guessTarget` Nothing) >=> mapFile env) targetStrs)+ >>= mapM relativize++ let targets = map (\t -> t { targetAllowObjCode = False }) targets'++ gmLog GmDebug "loadTargets" $+ text "Loading" <+>: fsep (map (text . showTargetId) targets)++ setTargets targets++ mg <- depanal [] False++ let interp = needsHscInterpreted mg+ target <- hscTarget <$> getSessionDynFlags+ when (interp && target /= HscInterpreted) $ do+ _ <- setSessionDynFlags . setHscInterpreted =<< getSessionDynFlags+ gmLog GmInfo "loadTargets" $ text "Target needs interpeter, switching to LinkInMemory/HscInterpreted. Perfectly normal if anything is using TemplateHaskell, QuasiQuotes or PatternSynonyms."++ target' <- hscTarget <$> getSessionDynFlags++ case target' of+ HscNothing -> do+ void $ load LoadAllTargets+ forM_ mg $+ handleSourceError (gmLog GmDebug "loadTargets" . text . show)+ . void . (parseModule >=> typecheckModule >=> desugarModule)+ HscInterpreted -> do+ void $ load LoadAllTargets+ _ -> error ("loadTargets: unsupported hscTarget")++ gmLog GmDebug "loadTargets" $ text "Loading done"++ where+ relativize (Target (TargetFile filePath phase) taoc src) = do+ crdl <- cradle+ let tid = TargetFile relativeFilePath phase+ relativeFilePath = makeRelative (cradleRootDir crdl) filePath+ return $ Target tid taoc src+ relativize tgt = return tgt++ showTargetId (Target (TargetModule s) _ _) = moduleNameString s+ showTargetId (Target (TargetFile s _) _ _) = s++needsHscInterpreted :: ModuleGraph -> Bool+needsHscInterpreted = any $ \ms ->+ let df = ms_hspp_opts ms in+#if __GLASGOW_HASKELL__ >= 800+ TemplateHaskell `xopt` df+ || QuasiQuotes `xopt` df+ || PatternSynonyms `xopt` df+#else+ Opt_TemplateHaskell `xopt` df+ || Opt_QuasiQuotes `xopt` df+#if __GLASGOW_HASKELL__ >= 708+ || (Opt_PatternSynonyms `xopt` df)+#endif+#endif++cabalResolvedComponents :: (IOish m) =>+ GhcModT m (Map ChComponentName (GmComponent 'GMCResolved (Set ModulePath)))+cabalResolvedComponents = do+ crdl@(Cradle{..}) <- cradle+ comps <- mapM (resolveEntrypoint crdl) =<< getComponents+ withAutogen $+ cached cradleRootDir (resolvedComponentsCache cradleDistDir) comps
+ core/GhcMod/Types.hs view
@@ -0,0 +1,406 @@+{-# LANGUAGE CPP, DeriveDataTypeable, DeriveFunctor, DeriveGeneric, RankNTypes,+ StandaloneDeriving, DefaultSignatures, FlexibleInstances, TemplateHaskell,+ GeneralizedNewtypeDeriving #-}+{-# OPTIONS_GHC -fno-warn-orphans -fno-warn-deprecations #-}+module GhcMod.Types (+ module GhcMod.Types+ , ModuleName+ , mkModuleName+ , moduleNameString+ ) where++import Control.Monad.Trans.Control (MonadBaseControl)+import Control.Monad.Error (Error(..))+import qualified Control.Monad.IO.Class as MTL+import Control.Exception (Exception)+import Control.Applicative+import Control.Concurrent+import Control.Monad+import Control.DeepSeq+import Data.Binary+import Data.Binary.Generic+import Data.Map (Map)+import qualified Data.Map as Map+import Data.Set (Set)+import qualified Data.Set as Set+import Data.Monoid+import Data.Maybe+import Data.Typeable (Typeable)+import Data.IORef+import Data.Label.Derive+import Distribution.Helper hiding (Programs(..))+import qualified Distribution.Helper as CabalHelper+import Exception (ExceptionMonad)+#if __GLASGOW_HASKELL__ < 708+import qualified MonadUtils as GHC (MonadIO(..))+#endif+import GHC (ModuleName, moduleNameString, mkModuleName)+import HscTypes (HscEnv)+import GHC.Generics+import Pretty (Doc)+import Prelude++import GhcMod.Caching.Types++-- | A constraint alias (-XConstraintKinds) to make functions dealing with+-- 'GhcModT' somewhat cleaner.+--+-- Basicially an @IOish m => m@ is a 'Monad' supporting arbitrary 'IO' and+-- exception handling. Usually this will simply be 'IO' but we parametrise it in+-- the exported API so users have the option to use a custom inner monad.+type IOish m = (Functor m, MonadIO m, MonadBaseControl IO m, ExceptionMonad m)+++-- MonadUtils of GHC 7.6 or earlier defines its own MonadIO.+-- MonadUtils of GHC 7.8 or later imports MonadIO in Monad.Control.IO.Class.+#if __GLASGOW_HASKELL__ < 708+type MonadIOC m = (GHC.MonadIO m, MTL.MonadIO m)+#else+type MonadIOC m = (MTL.MonadIO m)+#endif++class MonadIOC m => MonadIO m where+ liftIO :: IO a -> m a++-- | Output style.+data OutputStyle = LispStyle -- ^ S expression style.+ | PlainStyle -- ^ Plain textstyle.+ deriving (Show)++-- | The type for line separator. Historically, a Null string is used.+newtype LineSeparator = LineSeparator String deriving (Show)++data FileMapping = FileMapping {fmPath :: FilePath, fmTemp :: Bool}+ deriving (Eq, Show)++type FileMappingMap = Map FilePath FileMapping++data ProgramSource = ProgramSourceUser | ProgramSourceStack++data Programs = Programs {+ -- | @ghc@ program name.+ ghcProgram :: FilePath+ -- | @ghc-pkg@ program name.+ , ghcPkgProgram :: FilePath+ -- | @cabal@ program name.+ , cabalProgram :: FilePath+ -- | @stack@ program name.+ , stackProgram :: FilePath+ } deriving (Show)++data OutputOpts = OutputOpts {+ -- | Verbosity+ ooptLogLevel :: GmLogLevel+ , ooptStyle :: OutputStyle+ -- | Line separator string.+ , ooptLineSeparator :: LineSeparator+ -- | Stdout/err line multiplexing using prefix encoding. @fst@ is stdout,+ -- @snd@ is stderr prefix.+ , ooptLinePrefix :: Maybe (String, String)+ } deriving (Show)++data Options = Options {+ optOutput :: OutputOpts+ , optPrograms :: Programs+ -- | GHC command line options set on the @ghc-mod@ command line+ , optGhcUserOptions :: [GHCOption]+ , optFileMappings :: [(FilePath, Maybe FilePath)]+ , optEncoding :: String+ , optStackBuildDeps :: Bool+ } deriving (Show)++-- | A default 'Options'.+defaultOptions :: Options+defaultOptions = Options {+ optOutput = OutputOpts {+ ooptLogLevel = GmWarning+ , ooptStyle = PlainStyle+ , ooptLineSeparator = LineSeparator "\0"+ , ooptLinePrefix = Nothing+ }+ , optPrograms = Programs {+ ghcProgram = "ghc"+ , ghcPkgProgram = "ghc-pkg"+ , cabalProgram = "cabal"+ , stackProgram = "stack"+ }+ , optGhcUserOptions = []+ , optFileMappings = []+ , optEncoding = "UTF-8"+ , optStackBuildDeps = False+ }++----------------------------------------------------------------++data Project = CabalProject+ | SandboxProject+ | PlainProject+ | StackProject StackEnv+ deriving (Eq, Show, Ord)++isCabalHelperProject :: Project -> Bool+isCabalHelperProject StackProject {} = True+isCabalHelperProject CabalProject {} = True+isCabalHelperProject _ = False++data StackEnv = StackEnv {+ seDistDir :: FilePath+ , seBinPath :: [FilePath]+ , seSnapshotPkgDb :: FilePath+ , seLocalPkgDb :: FilePath+ } deriving (Eq, Show, Ord)++-- | The environment where this library is used.+data Cradle = Cradle {+ cradleProject :: Project+ -- | The directory where this library is executed.+ , cradleCurrentDir :: FilePath+ -- | The project root directory.+ , cradleRootDir :: FilePath+ -- | Per-Project temporary directory+ , cradleTempDir :: FilePath+ -- | The file name of the found cabal file.+ , cradleCabalFile :: Maybe FilePath+ -- | The build info directory.+ , cradleDistDir :: FilePath+ } deriving (Eq, Show, Ord)++data GmStream = GmOutStream | GmErrStream+ deriving (Show)++data GhcModEnv = GhcModEnv {+ gmOptions :: Options+ , gmCradle :: Cradle+ }++data GhcModOut = GhcModOut {+ gmoOptions :: OutputOpts+ , gmoChan :: Chan (Either (MVar ()) (GmStream, String))+ }++data GhcModLog = GhcModLog {+ gmLogLevel :: Maybe GmLogLevel,+ gmLogVomitDump :: Last Bool,+ gmLogMessages :: [(GmLogLevel, String, Doc)]+ } deriving (Show)++instance Monoid GhcModLog where+ mempty = GhcModLog (Just GmPanic) (Last Nothing) mempty+ GhcModLog ml vd ls `mappend` GhcModLog ml' vd' ls' =+ GhcModLog (ml' `mplus` ml) (vd `mappend` vd') (ls `mappend` ls')++data GmGhcSession = GmGhcSession {+ gmgsSession :: !(IORef HscEnv)+ }++data GhcModCaches = GhcModCaches {+ gmcPackageDbStack :: CacheContents ChCacheData [GhcPkgDb]+ , gmcMergedPkgOptions :: CacheContents ChCacheData [GHCOption]+ , gmcComponents :: CacheContents ChCacheData [GmComponent 'GMCRaw ChEntrypoint]+ , gmcResolvedComponents :: CacheContents+ [GmComponent 'GMCRaw (Set.Set ModulePath)]+ (Map.Map ChComponentName (GmComponent 'GMCResolved (Set.Set ModulePath)))+ }++data GhcModState = GhcModState {+ gmGhcSession :: !(Maybe GmGhcSession)+ , gmCaches :: !GhcModCaches+ , gmMMappedFiles :: !FileMappingMap+ }++defaultGhcModState :: GhcModState+defaultGhcModState =+ GhcModState n (GhcModCaches n n n n) Map.empty+ where n = Nothing++----------------------------------------------------------------++-- | GHC package database flags.+data GhcPkgDb = GlobalDb+ | UserDb+ | PackageDb String+ deriving (Eq, Show, Generic)++instance Binary GhcPkgDb where+ put = ggput . from+ get = to `fmap` ggget++-- | A single GHC command line option.+type GHCOption = String++-- | An include directory for modules.+type IncludeDir = FilePath++-- | Haskell expression.+newtype Expression = Expression { getExpression :: String }+ deriving (Show, Eq, Ord)++-- | Module name.+newtype ModuleString = ModuleString { getModuleString :: String }+ deriving (Show, Eq, Ord, Binary, NFData)++data GmLogLevel =+ GmSilent+ | GmPanic+ | GmException+ | GmError+ | GmWarning+ | GmInfo+ | GmDebug+ | GmVomit+ deriving (Eq, Ord, Enum, Bounded, Show, Read)++data GmModuleGraph = GmModuleGraph {+ gmgGraph :: Map ModulePath (Set ModulePath)+ } deriving (Eq, Ord, Show, Read, Generic, Typeable)++instance Binary GmModuleGraph where+ put GmModuleGraph {..} = put (mpim, graph)+ where+ mpim :: Map ModulePath Integer+ mpim = Map.fromList $ Map.keys gmgGraph `zip` [0..]+ graph :: Map Integer (Set Integer)+ graph = Map.map (Set.map mpToInt) $ Map.mapKeys mpToInt gmgGraph+ mpToInt :: ModulePath -> Integer+ mpToInt mp = fromJust $ Map.lookup mp mpim++ get = do+ (mpim :: Map ModulePath Integer, graph :: Map Integer (Set Integer)) <- get+ let impm = swapMap mpim+ intToMp i = fromJust $ Map.lookup i impm+ mpGraph :: Map ModulePath (Set ModulePath)+ mpGraph = Map.map (Set.map intToMp) $ Map.mapKeys intToMp graph+ return $ GmModuleGraph mpGraph+ where+ swapMap :: Ord v => Map k v -> Map v k+ swapMap = Map.fromList . map (\(x, y) -> (y, x)) . Map.toList++instance Monoid GmModuleGraph where+ mempty = GmModuleGraph mempty+ mappend (GmModuleGraph a) (GmModuleGraph a') =+ GmModuleGraph (Map.unionWith Set.union a a')++data GmComponentType = GMCRaw+ | GMCResolved+data GmComponent (t :: GmComponentType) eps = GmComponent {+ gmcHomeModuleGraph :: GmModuleGraph+ , gmcName :: ChComponentName+ , gmcGhcOpts :: [GHCOption]+ , gmcGhcPkgOpts :: [GHCOption]+ , gmcGhcSrcOpts :: [GHCOption]+ , gmcGhcLangOpts :: [GHCOption]+ , gmcRawEntrypoints :: ChEntrypoint+ , gmcEntrypoints :: eps+ , gmcSourceDirs :: [FilePath]+ } deriving (Eq, Ord, Show, Read, Generic, Functor)++instance Binary eps => Binary (GmComponent t eps) where+ put = ggput . from+ get = to `fmap` ggget++data ModulePath = ModulePath { mpModule :: ModuleName, mpPath :: FilePath }+ deriving (Eq, Ord, Show, Read, Generic, Typeable)+instance Binary ModulePath where+ put = ggput . from+ get = to `fmap` ggget++instance Binary ModuleName where+ get = mkModuleName <$> get+ put mn = put (moduleNameString mn)++instance Show ModuleName where+ show mn = "ModuleName " ++ show (moduleNameString mn)++instance Read ModuleName where+ readsPrec d =+ readParen+ (d > app_prec)+ (\r' -> [ (mkModuleName m, t)+ | ("ModuleName", s) <- lex r'+ , (m, t) <- readsPrec (app_prec + 1) s+ ])+ where+ app_prec = 10++data GhcModError+ = GMENoMsg+ -- ^ Unknown error++ | GMEString String+ -- ^ Some Error with a message. These are produced mostly by+ -- 'fail' calls on GhcModT.++ | GMECabalConfigure GhcModError+ -- ^ Configuring a cabal project failed.++ | GMEStackConfigure GhcModError+ -- ^ Configuring a stack project failed.++ | GMEStackBootstrap GhcModError+ -- ^ Bootstrapping @stack@ environment failed (process exited with failure)++ | GMECabalCompAssignment [(Either FilePath ModuleName, Set ChComponentName)]+ -- ^ Could not find a consistent component assignment for modules++ | GMEProcess String String [String] (Either Int GhcModError)+ -- ^ Launching an operating system process failed. Fields in+ -- order: function, command, arguments, (stdout, stderr, exitcode)++ | GMENoCabalFile+ -- ^ No cabal file found.++ | GMETooManyCabalFiles [FilePath]+ -- ^ Too many cabal files found.++ deriving (Eq,Show,Typeable)++instance Error GhcModError where+ noMsg = GMENoMsg+ strMsg = GMEString++instance Exception GhcModError++instance Binary CabalHelper.Programs where+ put = ggput . from+ get = to `fmap` ggget+instance Binary ChModuleName where+ put = ggput . from+ get = to `fmap` ggget+instance Binary ChComponentName where+ put = ggput . from+ get = to `fmap` ggget+instance Binary ChEntrypoint where+ put = ggput . from+ get = to `fmap` ggget++-- | Options for "lintWith" function+data LintOpts = LintOpts {+ optLintHlintOpts :: [String]+ -- ^ options that will be passed to hlint executable+ } deriving (Show)++-- | Default "LintOpts" instance+defaultLintOpts :: LintOpts+defaultLintOpts = LintOpts []++-- | Options for "browseWith" function+data BrowseOpts = BrowseOpts {+ optBrowseOperators :: Bool+ -- ^ If 'True', "browseWith" also returns operators.+ , optBrowseDetailed :: Bool+ -- ^ If 'True', "browseWith" also returns types.+ , optBrowseParents :: Bool+ -- ^ If 'True', "browseWith" also returns parents.+ , optBrowseQualified :: Bool+ -- ^ If 'True', "browseWith" will return fully qualified name+ } deriving (Show)++-- | Default "BrowseOpts" instance+defaultBrowseOpts :: BrowseOpts+defaultBrowseOpts = BrowseOpts False False False False++mkLabel ''GhcModCaches+mkLabel ''GhcModState+mkLabel ''Options+mkLabel ''OutputOpts+mkLabel ''Programs
+ core/GhcMod/Utils.hs view
@@ -0,0 +1,165 @@+-- ghc-mod: Happy Haskell Hacking+-- Copyright (C) 2015 Daniel Gröber <dxld ÄT darkboxed DOT org>+--+-- This program is free software: you can redistribute it and/or modify+-- it under the terms of the GNU Affero General Public License as published by+-- the Free Software Foundation, either version 3 of the License, or+-- (at your option) any later version.+--+-- This program is distributed in the hope that it will be useful,+-- but WITHOUT ANY WARRANTY; without even the implied warranty of+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+-- GNU Affero General Public License for more details.+--+-- You should have received a copy of the GNU Affero General Public License+-- along with this program. If not, see <http://www.gnu.org/licenses/>.++{-# LANGUAGE CPP #-}+{-# LANGUAGE DoAndIfThenElse #-}++module GhcMod.Utils (+ module GhcMod.Utils+ , module Utils+ , readProcess+ ) where++import Control.Applicative+import Data.Char+import qualified Data.Map as M+import Data.Maybe (fromMaybe)+import Data.Either (rights)+import Data.List (inits)+import Exception+import GhcMod.Error+import GhcMod.Types+import GhcMod.Monad.Types+import System.Directory+import System.Environment+import System.FilePath+import System.IO.Temp (createTempDirectory)+import System.Process (readProcess)+import Text.Printf++import Paths_ghc_mod (getLibexecDir, getBinDir)+import Utils+import Prelude++-- dropWhileEnd is not provided prior to base 4.5.0.0.+dropWhileEnd :: (a -> Bool) -> [a] -> [a]+dropWhileEnd p = foldr (\x xs -> if p x && null xs then [] else x : xs) []++withDirectory_ :: (MonadIO m, ExceptionMonad m) => FilePath -> m a -> m a+withDirectory_ dir action =+ gbracket+ (liftIO getCurrentDirectory)+ (liftIO . setCurrentDirectory)+ (\_ -> liftIO (setCurrentDirectory dir) >> action)++uniqTempDirName :: FilePath -> FilePath+uniqTempDirName dir =+ "ghc-mod" ++ map escapeDriveChar drive ++ map escapePathChar path+ where+ (drive, path) = splitDrive dir+ escapeDriveChar :: Char -> Char+ escapeDriveChar c+ | isAlphaNum c = c+ | otherwise = '-'+ escapePathChar :: Char -> Char+ escapePathChar c+ | c `elem` pathSeparators = '-'+ | otherwise = c++newTempDir :: FilePath -> IO FilePath+newTempDir _dir =+ flip createTempDirectory "ghc-mod" =<< getTemporaryDirectory++whenM :: Monad m => m Bool -> m () -> m ()+whenM mb ma = mb >>= flip when ma++-- | Returns the path to the currently running ghc-mod executable. With ghc<7.6+-- this is a guess but >=7.6 uses 'getExecutablePath'.+ghcModExecutable :: IO FilePath+ghcModExecutable = do+ exe <- getExecutablePath'+ stack <- lookupEnv "STACK_EXE"+ case takeBaseName exe of+ "spec" | Just _ <- stack ->+ (</> "ghc-mod") <$> getBinDir+ "spec" ->+ (</> "dist/build/ghc-mod/ghc-mod") <$> getCurrentDirectory+ "ghc-mod" ->+ return exe+ _ ->+ return $ takeDirectory exe </> "ghc-mod"++getExecutablePath' :: IO FilePath+#if __GLASGOW_HASKELL__ >= 706+getExecutablePath' = getExecutablePath+#else+getExecutablePath' = getProgName+#endif++canonFilePath :: FilePath -> IO FilePath+canonFilePath f = do+ p <- canonicalizePath f+ e <- doesFileExist p+ when (not e) $ error $ "canonFilePath: not a file: " ++ p+ return p++withMappedFile :: (IOish m, GmState m, GmEnv m) =>+ forall a. FilePath -> (FilePath -> m a) -> m a+withMappedFile file action = getCanonicalFileNameSafe file >>= lookupMMappedFile >>= runWithFile+ where+ runWithFile (Just to) = action $ fmPath to+ runWithFile _ = action file++getCanonicalFileNameSafe :: (IOish m, GmEnv m) => FilePath -> m FilePath+getCanonicalFileNameSafe fn = do+ let fn' = normalise fn+ pl <- liftIO $ rights <$> (mapM ((try :: IO FilePath -> IO (Either SomeException FilePath)) . canonicalizePath . joinPath) $ reverse $ inits $ splitPath' fn')+ return $+ if (length pl > 0)+ then joinPath $ (head pl):(drop (length pl - 1) (splitPath fn'))+ else error "Current dir doesn't seem to exist?"+ where+#if __GLASGOW_HASKELL__ < 710+ splitPath' = (".":) . splitPath+#else+ splitPath' = splitPath+#endif++mkRevRedirMapFunc :: (Functor m, GmState m, GmEnv m) => m (FilePath -> FilePath)+mkRevRedirMapFunc = do+ rm <- M.fromList <$> map (uncurry mf) <$> M.toList <$> getMMappedFiles+ crdl <- cradle+ return $ \key ->+ fromMaybe key+ $ makeRelative (cradleRootDir crdl)+ <$> M.lookup key rm+ where+ mf :: FilePath -> FileMapping -> (FilePath, FilePath)+ mf from to = (fmPath to, from)++findFilesWith' :: (FilePath -> IO Bool) -> [FilePath] -> String -> IO [FilePath]+findFilesWith' _ [] _ = return []+findFilesWith' f (d:ds) fileName = do+ let file = d </> fileName+ exist <- doesFileExist file+ b <- if exist then f file else return False+ if b then do+ files <- findFilesWith' f ds fileName+ return $ file : files+ else findFilesWith' f ds fileName+++-- Copyright : (c) The University of Glasgow 2001+-- | Make a path absolute by prepending the current directory (if it isn't+-- already absolute) and applying 'normalise' to the result.+--+-- If the path is already absolute, the operation never fails. Otherwise, the+-- operation may fail with the same exceptions as 'getCurrentDirectory'.+makeAbsolute' :: FilePath -> IO FilePath+makeAbsolute' = (normalise <$>) . absolutize+ where absolutize path -- avoid the call to `getCurrentDirectory` if we can+ | isRelative path = (</> path) <$> getCurrentDirectory+ | otherwise = return path
+ core/GhcMod/World.hs view
@@ -0,0 +1,55 @@+module GhcMod.World where++import GhcMod.GhcPkg+import GhcMod.PathsAndFiles+import GhcMod.Types+import GhcMod.Monad.Types+import GhcMod.Utils++import Control.Applicative+import Data.Maybe+import Data.Traversable hiding (mapM)+import System.FilePath ((</>))++import GHC.Paths (libdir)+import Prelude++data World = World {+ worldPackageCaches :: [TimedFile]+ , worldCabalFile :: Maybe TimedFile+ , worldCabalConfig :: Maybe TimedFile+ , worldCabalSandboxConfig :: Maybe TimedFile+ , worldMappedFiles :: FileMappingMap+ } deriving (Eq)++timedPackageCaches :: IOish m => GhcModT m [TimedFile]+timedPackageCaches = do+ fs <- mapM (liftIO . mightExist) . map (</> packageCache)+ =<< getPackageCachePaths libdir+ (liftIO . timeFile) `mapM` catMaybes fs++getCurrentWorld :: IOish m => GhcModT m World+getCurrentWorld = do+ crdl <- cradle+ pkgCaches <- timedPackageCaches+ mCabalFile <- liftIO $ timeFile `traverse` cradleCabalFile crdl+ mCabalConfig <- liftIO $ timeMaybe (setupConfigFile crdl)+ mCabalSandboxConfig <- liftIO $ timeMaybe (sandboxConfigFile crdl)+ mFileMap <- getMMappedFiles++ return World {+ worldPackageCaches = pkgCaches+ , worldCabalFile = mCabalFile+ , worldCabalConfig = mCabalConfig+ , worldCabalSandboxConfig = mCabalSandboxConfig+ , worldMappedFiles = mFileMap+ }++didWorldChange :: IOish m => World -> GhcModT m Bool+didWorldChange world = do+ (world /=) <$> getCurrentWorld++isYoungerThanSetupConfig :: FilePath -> World -> IO Bool+isYoungerThanSetupConfig file World {..} = do+ tfile <- timeFile file+ return $ worldCabalConfig < Just tfile
elisp/Makefile view
@@ -1,17 +1,17 @@ SRCS = ghc.el ghc-func.el ghc-doc.el ghc-comp.el ghc-check.el ghc-process.el \ ghc-command.el ghc-info.el ghc-ins-mod.el ghc-indent.el ghc-rewrite.el EMACS = emacs-DETECT = xemacs TEMPFILE = temp.el+TEMPFILE2 = temp2.el all: $(TEMPFILE) ghc.el $(EMACS) -batch -q -no-site-file -l ./$(TEMPFILE) -f ghc-compile rm -f $(TEMPFILE) -detect: $(TEMPFILE) ghc.el- $(EMACS) -batch -q -no-site-file -l ./$(TEMPFILE) -f ghc-compile- rm -f $(DETECT)+lint: $(TEMPFILE2) ghc.el+ $(EMACS) -batch -q -no-site-file -l ./$(TEMPFILE2) -f ghc-compile+ rm -f $(TEMPFILE2) $(TEMPFILE): @echo '(setq load-path (cons "." load-path))' >> $(TEMPFILE)@@ -19,8 +19,15 @@ @echo $(SRCS)| sed -e 's/\(ghc[^ ]*\.el\)/"\1"/g' >> $(TEMPFILE) @echo ')))' >> $(TEMPFILE) +$(TEMPFILE2):+ @echo '(setq load-path (cons "." load-path))' >> $(TEMPFILE2)+ @echo '(setq hack-local-variables-hook (lambda () (setq lexical-binding t)))' >> $(TEMPFILE2)+ @echo '(defun ghc-compile () (mapcar (lambda (x) (byte-compile-file x)) (list ' >> $(TEMPFILE2)+ @echo $(SRCS)| sed -e 's/\(ghc[^ ]*\.el\)/"\1"/g' >> $(TEMPFILE2)+ @echo ')))' >> $(TEMPFILE2)+ clean:- rm -f *.elc $(TEMPFILE)+ rm -f *.elc $(TEMPFILE) $(TEMPFILE2) VERSION = `grep version ghc.el | sed -e 's/[^0-9\.]//g'`
elisp/ghc-check.el view
@@ -20,7 +20,7 @@ :underline (:style wave :color "orangered")) (t :inherit error))- "Face used for marking error lines."+ "Face used for error lines." :group 'ghc) (defface ghc-face-warn@@ -28,7 +28,7 @@ :underline (:style wave :color "gold")) (t :inherit warning))- "Face used for marking warning lines."+ "Face used for warning lines." :group 'ghc) (defface ghc-face-hole@@ -36,7 +36,7 @@ :underline (:style wave :color "purple")) (t :inherit warning))- "Face used for marking hole lines."+ "Face used for hole lines." :group 'ghc) (defvar ghc-check-error-fringe (propertize "!" 'display '(left-fringe exclamation-mark)))@@ -46,27 +46,30 @@ (defvar ghc-check-hole-fringe (propertize "_" 'display '(left-fringe horizontal-bar))) (defvar ghc-display-error nil- "*An action to display errors/warnings for 'M-n' and 'M-p:+ "*How to display errors/warnings when using 'M-n' and 'M-p': -nil does not display errors/warnings.-'minibuffer displays errors/warnings in the minibuffer.-'other-buffer displays errors/warnings in the other buffer.+nil do not display errors/warnings.+'minibuffer display errors/warnings in the minibuffer.+'other-buffer display errors/warnings in a new buffer. ") (defvar ghc-display-hole 'other-buffer- "*An action to display hole information for 'C-c C-j' and 'C-c C-h'+ "*How to display hole information when using 'C-c C-j' and 'C-c C-h' -'minibuffer displays errors/warnings in the minibuffer.-'other-buffer displays errors/warnings in the other buffer"+'minibuffer display errors/warnings in the minibuffer.+'other-buffer display errors/warnings in the a new buffer" ) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun ghc-check-syntax () (interactive)- (ghc-with-process (ghc-check-send)- 'ghc-check-callback- (lambda () (setq mode-line-process " -:-"))))+ ;; Only check syntax of visible buffers+ (when (and (buffer-file-name)+ (file-exists-p (buffer-file-name)))+ (ghc-with-process (ghc-check-send)+ 'ghc-check-callback+ (lambda () (setq mode-line-process " -:-"))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -129,10 +132,10 @@ (defun ghc-to-info (errs) ;; [^\t] to include \n. (let ((regex "^\\([^\n]*\\):\\([0-9]+\\):\\([0-9]+\\): *\\([^\t]+\\)")- info infos)+ infos) (dolist (err errs (nreverse infos)) (when (string-match regex err)- (let* ((file (expand-file-name (match-string 1 err))) ;; for Windows+ (let* ((file (expand-file-name (match-string 1 err) ghc-process-root)) ;; for Windows (line (string-to-number (match-string 2 err))) (coln (string-to-number (match-string 3 err))) (msg (match-string 4 err))@@ -167,18 +170,21 @@ ;; If this is a bottleneck for a large code, let's fix. (goto-char (point-min)) (cond- ((and (string= ofile file) hole)- (forward-line (1- line))- (forward-char (1- coln))- (setq beg (point))- (forward-char (length hole))- (setq end (point)))- ((string= ofile file)- (forward-line (1- line))- (while (eq (char-after) 32) (forward-char))- (setq beg (point))- (forward-line)- (setq end (1- (point))))+ ((file-equal-p ofile file)+ (if hole+ (progn+ (forward-line (1- line))+ (forward-char (1- coln))+ (setq beg (point))+ (forward-char (length hole))+ (setq end (point)))+ (progn+ (forward-line (1- line))+ (forward-char (1- coln))+ (setq beg (point))+ (forward-sexp)+ ;; (skip-chars-forward "^[:space:]" (line-end-position))+ (setq end (point))))) (t (setq beg (point)) (forward-line)@@ -285,14 +291,13 @@ (let ((file-msgs (ghc-get-only-holes))) (if (null file-msgs) (message "No holes")- (let ((file (ghc-file-msgs-get-file file-msgs))- (msgs (ghc-file-msgs-get-msgs file-msgs)))+ (let ((msgs (ghc-file-msgs-get-msgs file-msgs))) (ghc-display nil (lambda () (progn (mapc (lambda (x) (insert x "\n\n")) msgs)- (buttonize-buffer)) ))))))+ (buttonize-buffer)))))))) (defun ghc-display-holes-to-minibuf () (let ((file-msgs (ghc-get-only-holes)))@@ -410,6 +415,10 @@ (let ((old (match-string 1 data)) (new (match-string 2 data))) (ghc-check-replace old new)))+ ((string-match "Found hole .\\(_[_[:alnum:]]*\\). with type: \\([^\t\n]+\\)" data)+ (let ((old (match-string 1 data))+ (new (match-string 2 data)))+ (ghc-check-replace old new))) (t (setq ret nil))))))) @@ -465,7 +474,7 @@ (forward-line) (re-search-forward "^$" nil t) (insert fn)- (dotimes (i arity)+ (dotimes (_i arity) (insert " _")) (insert " = error \"" fn "\"\n")))))
elisp/ghc-command.el view
@@ -53,7 +53,7 @@ (let ((inhibit-field-text-motion t)) (sort-subr nil 'forward-line 'end-of-line (lambda ()- (re-search-forward "^import\\( *qualified\\)? *" nil t)+ (re-search-forward "^import +\\(qualified\\)? *" nil t) nil) 'end-of-line)) (ghc-merge-lines))))@@ -64,7 +64,7 @@ (while (not (eolp)) ;; qualified modlues are not merged at this moment. ;; fixme if it is improper.- (if (looking-at "^import *\\([A-Z][^ \n]+\\) *(\\(.*\\))$")+ (if (looking-at "^import +\\([A-Z][^ \n]+\\) *(\\(.*\\))$") (let ((mod (match-string-no-properties 1)) (syms (match-string-no-properties 2)) (beg (point)))@@ -73,7 +73,7 @@ (forward-line))))) (defun ghc-merge-line (beg mod syms)- (let ((regex (concat "^import *" (regexp-quote mod) " *(\\(.*\\))$"))+ (let ((regex (concat "^import +" (regexp-quote mod) " *(\\(.*\\))$")) duplicated) (while (looking-at regex) (setq duplicated t)
elisp/ghc-comp.el view
@@ -127,7 +127,7 @@ (interactive) (if (ghc-should-scroll) (ghc-scroll-completion-buffer)- (ghc-try-complete)))+ (ghc-try-complete))) (defun ghc-should-scroll () (let ((window (ghc-completion-window)))@@ -265,7 +265,7 @@ (let (ret) (save-excursion (goto-char (point-min))- (while (re-search-forward "^import\\( *qualified\\)? +\\([^\n ]+\\)" nil t)+ (while (re-search-forward "^import +\\(qualified\\)? *\\([^\n ]+\\)" nil t) (ghc-add ret (match-string-no-properties 2)) (forward-line))) ret))
elisp/ghc-doc.el view
@@ -10,6 +10,17 @@ (require 'ghc-comp) (require 'ghc-info) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+;;;+;;; Customize Variables+;;;++(defcustom ghc-doc-browser-function #'browse-url+ "Function used to browse documentation."+ :type '(radio (function-item browse-url)+ (function-item ghc-browse-url-safari))+ :group 'ghc-mod)+ ;;; Code: (defun ghc-browse-document (&optional haskell-org)@@ -22,40 +33,49 @@ (setq expr (ghc-read-expression expr0)) (setq info (ghc-get-info expr0)) (setq mod (ghc-extact-module-from-info info)))- (setq pkg-ver-path (ghc-resolve-document-path mod))- (if (and pkg-ver-path mod)+ (setq pkg-ver-path (and mod (ghc-resolve-document-path mod)))+ (if pkg-ver-path (ghc-display-document pkg-ver-path mod haskell-org expr)- (message "No document found"))))+ (message "No documentation found")))) (ghc-defstruct pkg-ver-path pkg ver path) (defun ghc-resolve-document-path (mod)- (with-temp-buffer- (ghc-call-process ghc-module-command nil t nil "doc" mod)- (goto-char (point-min))- (when (looking-at "^\\([^ ]+\\)-\\([0-9]*\\(\\.[0-9]+\\)*\\) \\(.*\\)$")- (ghc-make-pkg-ver-path- :pkg (match-string-no-properties 1)- :ver (match-string-no-properties 2)- :path (match-string-no-properties 4)))))+ (let ((root ghc-process-root))+ (with-temp-buffer+ (let ((default-directory root))+ (ghc-call-process ghc-module-command nil t nil "doc" mod))+ (goto-char (point-min))+ (when (looking-at "^\\([^ ]+\\)-\\([0-9]*\\(\\.[0-9]+\\)*\\) \\(.*\\)$")+ (ghc-make-pkg-ver-path+ :pkg (match-string-no-properties 1)+ :ver (match-string-no-properties 2)+ :path (match-string-no-properties 4)))))) (defconst ghc-doc-local-format "file://%s/%s.html") (defconst ghc-doc-hackage-format "http://hackage.haskell.org/packages/archive/%s/%s/doc/html/%s.html") +(defun ghc-browse-url-safari (uri &rest _args)+"Open a URI in Safari using AppleScript. This preserves anchors."+ (let ((script (format "+tell application \"Safari\"+ open location \"%s\"+ activate+end tell" uri)))+ (do-applescript script)))+ (defun ghc-display-document (pkg-ver-path mod haskell-org &optional symbol)- (let* ((mod- (ghc-replace-character mod ?. ?-))- (pkg (ghc-pkg-ver-path-get-pkg pkg-ver-path))+ (let* ((pkg (ghc-pkg-ver-path-get-pkg pkg-ver-path))+ (mod- (ghc-replace-character mod ?. ?-)) (ver (ghc-pkg-ver-path-get-ver pkg-ver-path)) (path (ghc-pkg-ver-path-get-path pkg-ver-path))- (pkg-with-ver (format "%s-%s" pkg ver)) (local (format ghc-doc-local-format path mod-)) (remote (format ghc-doc-hackage-format pkg ver mod-)) (file (format "%s/%s.html" path mod-)) (url0 (if (or haskell-org (not (file-exists-p file))) remote local)) (url (if symbol (ghc-add-anchor url0 symbol) url0)))- ;; Mac's "open" removes the anchor from "file://", sigh.- (browse-url url)))+ (funcall ghc-doc-browser-function url))) (defun ghc-add-anchor (url symbol) (let ((case-fold-search nil))@@ -75,7 +95,7 @@ (apply 'concat (nreverse acc)))) (defun ghc-extact-module-from-info (info)- (when (string-match "\`\\([^']+\\)'" info)+ (when (string-match "[`\u2018]\\([^'\u2019]+\\)['\u2019]" info) (match-string 1 info))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;@@ -93,7 +113,7 @@ (read-from-minibuffer "Module name: " def ghc-input-map)) (defun ghc-read-expression (def)- (read-from-minibuffer "Expression: " def ghc-input-map))+ (read-from-minibuffer "Identifier: " def ghc-input-map)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
elisp/ghc-func.el view
@@ -18,9 +18,10 @@ (defun ghc-replace-character (string from to) "Replace characters equal to FROM to TO in STRING." (let ((ret (copy-sequence string)))- (dotimes (cnt (length ret) ret)+ (dotimes (cnt (length ret)) (if (char-equal (aref ret cnt) from)- (aset ret cnt to)))))+ (aset ret cnt to)))+ ret)) (defun ghc-replace-character-buffer (from-c to-c) (let ((from (char-to-string from-c))@@ -66,7 +67,7 @@ (dolist (lst lol) (dolist (key lst) (puthash key key hash)))- (maphash (lambda (key val) (ghc-add ret key)) hash)+ (maphash (lambda (key _val) (ghc-add ret key)) hash) ret)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;@@ -90,8 +91,9 @@ (condition-case nil (let ((m (set-marker (make-marker) 1 (current-buffer))) ret)- (dotimes (i n (nreverse ret))- (ghc-add ret (read m))))+ (dotimes (_i n)+ (ghc-add ret (read m)))+ (nreverse ret)) (error ())))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;@@ -108,10 +110,11 @@ (defun ghc-keyword-number-pair (spec) (let ((len (length spec)) key ret)- (dotimes (i len (nreverse ret))+ (dotimes (i len) (setq key (intern (concat ":" (symbol-name (car spec))))) (setq ret (cons (cons key i) ret))- (setq spec (cdr spec)))))+ (setq spec (cdr spec)))+ (nreverse ret))) (defmacro ghc-defstruct (type &rest spec) `(progn@@ -182,9 +185,19 @@ (funcall ins-func) (goto-char (point-min)) (if (not fontify)- (turn-off-haskell-font-lock)+ ;; turn-off-haskell-font-lock has been removed from haskell-mode+ ;; test if the function is defined in our version+ (if (fboundp 'turn-off-haskell-font-lock)+ (turn-off-haskell-font-lock)+ ;; it's not defined, fallback on font-lock-mode+ (font-lock-mode -1)) (haskell-font-lock-defaults-create)- (turn-on-haskell-font-lock)))+ ;; turn-on-haskell-font-lock has been removed from haskell-mode+ ;; test if the function is defined in our version+ (if (fboundp 'turn-on-haskell-font-lock)+ (turn-on-haskell-font-lock)+ ;; it's not defined, fallback on font-lock-mode+ (turn-on-font-lock)))) (display-buffer buf '((display-buffer-reuse-window display-buffer-pop-up-window))))))@@ -194,12 +207,13 @@ (defun ghc-run-ghc-mod (cmds &optional prog) (let ((target (or prog ghc-module-command))) (ghc-executable-find target- (let ((cdir default-directory))+ (let ((cdir (or ghc-process-root ;; ghc-mod version/debug+ default-directory))) ;; ghc-mod root (with-temp-buffer- (cd cdir)- (apply 'ghc-call-process target nil t nil- (append (ghc-make-ghc-options) cmds))- (buffer-substring (point-min) (1- (point-max))))))))+ (let ((default-directory cdir))+ (apply 'ghc-call-process target nil '(t nil) nil+ (append (ghc-make-ghc-options) cmds))+ (buffer-substring (point-min) (1- (point-max))))))))) (defmacro ghc-executable-find (cmd &rest body) ;; (declare (indent 1))
elisp/ghc-indent.el view
@@ -10,11 +10,11 @@ (defvar ghc-indent-offset 4) -(defun ghc-make-indent-shallower (beg end)+(defun ghc-make-indent-shallower (_beg _end) (interactive "r") (indent-rigidly (region-beginning) (region-end) (- ghc-indent-offset))) -(defun ghc-make-indent-deeper (beg end)+(defun ghc-make-indent-deeper (_beg _end) (interactive "r") (indent-rigidly (region-beginning) (region-end) ghc-indent-offset))
elisp/ghc-info.el view
@@ -63,7 +63,7 @@ (cons 'ghc-type-clear-overlay after-change-functions)) (add-hook 'post-command-hook 'ghc-type-post-command-hook)) -(defun ghc-type-clear-overlay (&optional beg end len)+(defun ghc-type-clear-overlay (&optional _beg _end _len) (when (overlayp ghc-type-overlay) (ghc-type-set-ix 0) (ghc-type-set-point 0)@@ -82,7 +82,7 @@ (if (null tinfos) (progn (ghc-type-clear-overlay)- (message "Cannot guess type"))+ (message "Cannot determine type")) (let* ((tinfo (nth (ghc-type-get-ix) tinfos)) (type (ghc-tinfo-get-info tinfo)) (beg-line (ghc-tinfo-get-beg-line tinfo))@@ -111,13 +111,7 @@ (cn (int-to-string (1+ (current-column)))) (file (buffer-file-name)) (cmd (format "type %s %s %s\n" file ln cn)))- (ghc-sync-process cmd nil 'ghc-type-fix-string)))--(defun ghc-type-fix-string ()- (save-excursion- (goto-char (point-min))- (while (search-forward "[Char]" nil t)- (replace-match "String"))))+ (ghc-sync-process cmd nil))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;@@ -127,7 +121,7 @@ (defun ghc-expand-th () (interactive) (let* ((file (buffer-file-name))- (cmds (list "expand" file "-b" "\n"))+ (cmds (list "-b" "\n" "expand" file)) (source (ghc-run-ghc-mod cmds))) (when source (ghc-display
elisp/ghc-ins-mod.el view
@@ -56,7 +56,7 @@ (defun ghc-goto-module-position () (goto-char (point-max))- (if (re-search-backward "^import" nil t)+ (if (re-search-backward "^import +" nil t) (ghc-goto-empty-line) (if (not (re-search-backward "^module" nil t)) (goto-char (point-min))
elisp/ghc-pkg.el view
@@ -2,4 +2,4 @@ "ghc" 2.0.0 "Sub mode for Haskell mode"- nil)+ '((haskell-mode "13.0")))
elisp/ghc-process.el view
@@ -1,3 +1,4 @@+;;; -*- lexical-binding: t -*- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; ;;; ghc-process.el@@ -10,94 +11,179 @@ (require 'ghc-func) +(defvar ghc-debug-options nil)+;; (setq ghc-debug-options '("-v9"))+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defvar ghc-process-running nil)+(defvar ghc-process-file-mapping nil) (defvar-local ghc-process-process-name nil) (defvar-local ghc-process-original-buffer nil) (defvar-local ghc-process-original-file nil)-(defvar-local ghc-process-callback nil)-(defvar-local ghc-process-hook nil)+(defvar-local ghc-process-root nil) -(defvar ghc-interactive-command "ghc-modi")+(defvar ghc-command "ghc-mod") +(defvar ghc-report-errors t "Report GHC errors to *GHC Error* buffer")+(defvar ghc-error-buffer "*GHC Error*")+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun ghc-get-project-root () (ghc-run-ghc-mod '("root"))) -(defun ghc-with-process (cmd callback &optional hook1 hook2)+(defun ghc-with-process (cmd async-after-callback &optional sync-before-hook) (unless ghc-process-process-name (setq ghc-process-process-name (ghc-get-project-root))) (when (and ghc-process-process-name (not ghc-process-running)) (setq ghc-process-running t)- (if hook1 (funcall hook1))+ (if sync-before-hook (funcall sync-before-hook)) (let* ((cbuf (current-buffer)) (name ghc-process-process-name)- (buf (get-buffer-create (concat " ghc-modi:" name)))+ (root (file-name-as-directory ghc-process-process-name))+ (buf (get-buffer-create (concat " ghc-mod:" name))) (file (buffer-file-name)) (cpro (get-process name)))+ ;; setting root in the original buffer, sigh+ (setq ghc-process-root root) (ghc-with-current-buffer buf (setq ghc-process-original-buffer cbuf) (setq ghc-process-original-file file)- (setq ghc-process-callback callback)- (setq ghc-process-hook hook2)- (erase-buffer)- (let ((pro (ghc-get-process cpro name buf)))- (process-send-string pro cmd)+ (setq ghc-process-root root)+ (let ((pro (ghc-get-process cpro name buf root))+ (map-cmd (format "map-file %s\n" file)))+; (unmap-cmd (format "unmap-file %s\n" file)))+ (when (buffer-modified-p cbuf)+ (setq ghc-process-file-mapping t)+ (setq ghc-process-async-after-callback nil)+ (erase-buffer)+ (when ghc-debug+ (ghc-with-debug-buffer+ (insert (format "%% %s" map-cmd))+ (insert "CONTENTS + EOT\n")))+ (process-send-string pro map-cmd)+ (with-current-buffer cbuf+ (save-restriction+ (widen)+ (process-send-region pro (point-min) (point-max))))+ (process-send-string pro "\n\004\n")+ (condition-case nil+ (let ((inhibit-quit nil))+ (while ghc-process-file-mapping+ (accept-process-output pro 0.1 nil t)))+ (quit+ (setq ghc-process-running nil)+ (setq ghc-process-file-mapping nil))))+ ;; command+ (setq ghc-process-async-after-callback async-after-callback)+ (erase-buffer) (when ghc-debug (ghc-with-debug-buffer (insert (format "%% %s" cmd))))+ (process-send-string pro cmd)++ ;;; this needs to be done asyncrounously after the command actually+ ;;; finished, gah+ ;; (when do-map-file+ ;; (when ghc-debug+ ;; (ghc-with-debug-buffer+ ;; (insert (format "%% %s" unmap-cmd))))+ ;; (process-send-string pro unmap-cmd))+ pro))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defun ghc-get-process (cpro name buf)+(defun ghc-get-process (cpro name buf root) (cond ((not cpro)- (ghc-start-process name buf))+ (ghc-start-process name buf root)) ((not (eq (process-status cpro) 'run)) (delete-process cpro)- (ghc-start-process name buf))+ (ghc-start-process name buf root)) (t cpro))) -(defun ghc-start-process (name buf)- (let* ((opts (append '("-b" "\n" "-l") (ghc-make-ghc-options)))- (pro (apply 'start-file-process name buf ghc-interactive-command opts)))+(defun ghc-start-process (name buf root)+ (let* ((default-directory root)+ (process-connection-type nil) ;; using PIPE due to ^D+ (opts (append ghc-debug-options+ '("-b" "\n" "-l" "--line-prefix=O: ,E: ")+ (ghc-make-ghc-options)+ '("legacy-interactive")))+ (pro (apply 'start-process name buf ghc-command opts))) (set-process-filter pro 'ghc-process-filter) (set-process-sentinel pro 'ghc-process-sentinel) (set-process-query-on-exit-flag pro nil) pro)) (defun ghc-process-filter (process string)- (let ((pbuf (process-buffer process)))+ (let* ((pbuf (process-buffer process))+ (tbufname (concat " tmp " (buffer-name pbuf)))+ tbuf) (if (not (get-buffer pbuf)) (setq ghc-process-running nil) ;; just in case- (ghc-with-current-buffer (process-buffer process)- (goto-char (point-max))- (insert string)+ (ghc-with-current-buffer pbuf+ (when ghc-debug+ (ghc-with-debug-buffer+ (insert string)))+ (with-current-buffer (get-buffer-create tbufname)+ (setq tbuf (current-buffer))+ (goto-char (point-max))+ (insert string)+ (goto-char (point-min))+ (let ((cont t) end out)+ (while (and cont (not (eobp)) ghc-process-running)+ (cond+ ((looking-at "^O: ")+ (setq out t))+ ((looking-at "^E: ")+ (setq out nil))+ (t+ (setq cont nil)))+ (when cont+ (forward-line)+ (unless (bolp) (setq cont nil)))+ (when cont+ (delete-region 1 4)+ (setq end (point))+ (if out+ (with-current-buffer pbuf+ (goto-char (point-max))+ (insert-buffer-substring tbuf 1 end))+ (when ghc-report-errors+ (with-current-buffer (get-buffer-create ghc-error-buffer)+ (setq buffer-read-only t)+ (let* ((buffer-read-only nil)+ (inhibit-read-only t)+ (cbuf (current-buffer))+ cwin)+ (unless (get-buffer-window cbuf) (display-buffer cbuf))+ (setq cwin (get-buffer-window cbuf))+ (with-selected-window cwin+ (goto-char (point-max))+ (insert-buffer-substring tbuf 1 end)+ (set-buffer-modified-p nil))+ (redisplay)))))+ (delete-region 1 end)))))+ (goto-char (point-max)) (forward-line -1) (cond ((looking-at "^OK$")- (if ghc-process-hook (funcall ghc-process-hook))- (goto-char (point-min))- (funcall ghc-process-callback 'ok)- (when ghc-debug- (let ((cbuf (current-buffer)))- (ghc-with-debug-buffer- (insert-buffer-substring cbuf))))- (setq ghc-process-running nil))+ (delete-region (point) (point-max))+ (setq ghc-process-file-mapping nil)+ (when ghc-process-async-after-callback+ (goto-char (point-min))+ (funcall ghc-process-async-after-callback 'ok)+ (setq ghc-process-running nil))) ((looking-at "^NG ")- (funcall ghc-process-callback 'ng)- (when ghc-debug- (let ((cbuf (current-buffer)))- (ghc-with-debug-buffer- (insert-buffer-substring cbuf))))+ (funcall ghc-process-async-after-callback 'ng) (setq ghc-process-running nil))))))) -(defun ghc-process-sentinel (process event)- (setq ghc-process-running nil))+(defun ghc-process-sentinel (_process _event)+ (setq ghc-process-running nil)+ (setq ghc-process-file-mapping nil)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -105,24 +191,23 @@ (defvar ghc-process-num-of-results nil) (defvar ghc-process-results nil) -(defun ghc-sync-process (cmd &optional n hook)+(defun ghc-sync-process (cmd &optional n) (unless ghc-process-running (setq ghc-process-rendezvous nil) (setq ghc-process-results nil) (setq ghc-process-num-of-results (or n 1))- (let ((pro (ghc-with-process cmd 'ghc-process-callback nil hook)))+ (let ((pro (ghc-with-process cmd 'ghc-sync-process-callback nil)))+ ;; ghc-process-running is now t.+ ;; But if the process exits abnormally, it is set to nil. (condition-case nil- (while (null ghc-process-rendezvous)- ;; 0.01 is too fast for Emacs 24.4.- ;; (sit-for 0.1 t) may get stuck when tooltip is displayed.- (sit-for 0.1)- ;; (discard-input) avoids getting stuck.- (discard-input))+ (let ((inhibit-quit nil))+ (while (and (null ghc-process-rendezvous) ghc-process-running)+ (accept-process-output pro 0.1 nil t))) (quit (setq ghc-process-running nil)))) ghc-process-results)) -(defun ghc-process-callback (status)+(defun ghc-sync-process-callback (status) (cond ((eq status 'ok) (let* ((n ghc-process-num-of-results)@@ -139,11 +224,12 @@ (defun ghc-kill-process () (interactive)- (let* ((name ghc-process-process-name)- (cpro (if name (get-process name))))- (if (not cpro)- (message "No process")- (delete-process cpro)- (message "A process was killed"))))+ (when (eq major-mode 'haskell-mode)+ (let* ((name ghc-process-process-name)+ (cpro (if name (get-process name))))+ (if (not cpro)+ (message "No ghc-mod process")+ (delete-process cpro)+ (message "ghc-mod process was killed"))))) (provide 'ghc-process)
elisp/ghc-rewrite.el view
@@ -125,13 +125,10 @@ (ghc-display nil (lambda () (insert "Possible completions:\n")- (mapc - (lambda (x) - (let* (; (ins1 (insert "- "))- (pos-begin (point))- (ins (insert x))- (pos-end (point))- (ins3 (insert "\n")))+ (mapc+ (lambda (_x)+ (let ((pos-begin (point))+ (pos-end (point))) (make-button pos-begin pos-end :type 'auto-button))) (ghc-sinfo-get-info info)))) (select-window (ghc-auto-completion-window))))
elisp/ghc.el view
@@ -28,8 +28,10 @@ (< emacs-minor-version minor))) (error "ghc-mod requires at least Emacs %d.%d" major minor))) -(defconst ghc-version "5.1.0.2")+(defconst ghc-version "5.8.0.0") +(defgroup ghc-mod '() "ghc-mod customization")+ ;; (eval-when-compile ;; (require 'haskell-mode)) @@ -115,6 +117,7 @@ (define-key haskell-mode-map ghc-next-hole-key 'ghc-goto-next-hole) (ghc-comp-init) (setq ghc-initialized t)+ (add-hook 'kill-buffer-hook 'ghc-kill-process) (defadvice save-buffer (after ghc-check-syntax-on-save activate) "Check syntax with GHC when a haskell-mode buffer is saved." (when (eq 'haskell-mode major-mode) (ghc-check-syntax))))@@ -130,26 +133,26 @@ (let ((el-path (locate-file "ghc.el" load-path)) (ghc-path (executable-find "ghc")) ;; FIXME (ghc-mod-path (executable-find ghc-module-command))- (ghc-modi-path (executable-find ghc-interactive-command)) (el-ver ghc-version) (ghc-ver (ghc-run-ghc-mod '("--version") "ghc")) (ghc-mod-ver (ghc-run-ghc-mod '("version")))- (ghc-modi-ver (ghc-run-ghc-mod '("version") ghc-interactive-command))- (path (getenv "PATH")))+ (path (getenv "PATH"))+ (debug (ghc-run-ghc-mod '("debug")))) ;; before switching buffers. (switch-to-buffer (get-buffer-create "**GHC Debug**")) (erase-buffer) (insert "Path: check if you are using intended programs.\n") (insert (format "\t ghc.el path: %s\n" el-path)) (insert (format "\t ghc-mod path: %s\n" ghc-mod-path))- (insert (format "\tghc-modi path: %s\n" ghc-modi-path)) (insert (format "\t ghc path: %s\n" ghc-path))- (insert "\nVersion: all versions must be the same.\n")+ (insert "\nVersion: all GHC versions must be the same.\n") (insert (format "\t ghc.el version %s\n" el-ver)) (insert (format "\t %s\n" ghc-mod-ver))- (insert (format "\t%s\n" ghc-modi-ver)) (insert (format "\t%s\n" ghc-ver)) (insert "\nEnvironment variables:\n")- (insert (format "\tPATH=%s\n" path))))+ (insert (format "\tPATH=%s\n" path))+ (insert "\nThe result of \"ghc-mod debug\":\n")+ (insert debug)+ (goto-char (point-min)))) (defun ghc-insert-template-or-signature (&optional flag) (interactive "P")
ghc-mod.cabal view
@@ -1,162 +1,272 @@ Name: ghc-mod-Version: 5.1.0.2-Author: Kazu Yamamoto <kazu@iij.ad.jp>- Daniel Gröber <dxld@darkboxed.org>- Alejandro Serrano <trupill@gmail.com>-Maintainer: Kazu Yamamoto <kazu@iij.ad.jp>-License: BSD3+Version: 5.8.0.0+Author: Kazu Yamamoto <kazu@iij.ad.jp>,+ Daniel Gröber <dxld@darkboxed.org>,+ Alejandro Serrano <trupill@gmail.com>,+ Nikolay Yakimov <root@livid.pp.ru>+Maintainer: Daniel Gröber <dxld@darkboxed.org>+License: AGPL-3 License-File: LICENSE-Homepage: http://www.mew.org/~kazu/proj/ghc-mod/-Synopsis: Happy Haskell Programming-Description: The ghc-mod command is a backend command to enrich- Haskell programming on editors including- Emacs, Vim, and Sublime.- The ghc-mod command is based on ghc-mod library- which is a wrapper of GHC API.- This package includes the ghc-mod command,- the ghc-mod library, and Emacs front-end- (for historical reasons).- For more information, please see its home page.+License-Files: COPYING.BSD3 COPYING.AGPL3+Homepage: https://github.com/DanielG/ghc-mod+Synopsis: Happy Haskell Hacking+Description:+ ghc-mod is a backend program to enrich Haskell programming in editors. It+ strives to offer most of the features one has come to expect from modern IDEs+ in any editor. -Category: Development-Cabal-Version: >= 1.10-Build-Type: Simple-Data-Dir: elisp-Data-Files: Makefile ghc.el ghc-func.el ghc-doc.el ghc-comp.el- ghc-check.el ghc-process.el ghc-command.el ghc-info.el- ghc-ins-mod.el ghc-indent.el ghc-pkg.el ghc-rewrite.el+ ghc-mod provides a library for other haskell programs to use as well as a+ standalone program for easy editor integration. All of the fundamental+ functionality of the frontend program can be accessed through the library+ however many implementation details are hidden and if you want to+ significantly extend ghc-mod you should submit these changes upstream instead+ of implementing them on top of the library.+++Category: GHC, Development+Cabal-Version: >= 1.18+Build-Type: Custom+Data-Files: elisp/Makefile+ elisp/*.el Extra-Source-Files: ChangeLog- test/data/*.cabal- test/data/*.hs- test/data/cabal.sandbox.config.in- test/data/.cabal-sandbox/i386-osx-ghc-7.6.3-packages.conf.d/Cabal-1.18.1.3-2b161c6bf77657aa17e1681d83cb051b.conf+ README.md+ core/GhcMod/Monad/Compat.hs_h+ test/data/annotations/*.hs test/data/broken-cabal/*.cabal- test/data/broken-cabal/cabal.sandbox.config.in- test/data/broken-sandbox/*.cabal test/data/broken-sandbox/cabal.sandbox.config- test/data/cabal-flags/*.cabal+ test/data/broken-sandbox/dummy.cabal+ test/data/cabal-flags/cabal-flags.cabal+ test/data/cabal-project/*.cabal+ test/data/cabal-project/*.hs+ test/data/cabal-project/.cabal-sandbox/i386-osx-ghc-7.6.3-packages.conf.d/Cabal-1.18.1.3-2b161c6bf77657aa17e1681d83cb051b.conf+ test/data/cabal-project/subdir1/subdir2/dummy+ test/data/case-split/*.hs+ test/data/check-packageid/.cabal-sandbox/i386-osx-ghc-7.6.3-packages.conf.d/template-haskell-2.8.0.0-32d4f24abdbb6bf41272b183b2e23e9c.conf test/data/check-test-subdir/*.cabal test/data/check-test-subdir/src/Check/Test/*.hs test/data/check-test-subdir/test/*.hs test/data/check-test-subdir/test/Bar/*.hs- test/data/check-packageid/cabal.sandbox.config.in- test/data/check-packageid/.cabal-sandbox/i386-osx-ghc-7.6.3-packages.conf.d/template-haskell-2.8.0.0-32d4f24abdbb6bf41272b183b2e23e9c.conf- test/data/duplicate-pkgver/cabal.sandbox.config.in test/data/duplicate-pkgver/duplicate-pkgver.cabal test/data/duplicate-pkgver/.cabal-sandbox/i386-osx-ghc-7.6.3-packages.conf.d/template-haskell-1.0-7c59d13f32294d1ef6dc6233c24df961.conf test/data/duplicate-pkgver/.cabal-sandbox/i386-osx-ghc-7.6.3-packages.conf.d/template-haskell-2.8.0.0-14e543bdae2da4d2aeff5386892c9112.conf test/data/duplicate-pkgver/.cabal-sandbox/i386-osx-ghc-7.6.3-packages.conf.d/template-haskell-2.8.0.0-32d4f24abdbb6bf41272b183b2e23e9c.conf- test/data/pattern-synonyms/*.cabal- test/data/pattern-synonyms/*.hs+ test/data/foreign-export/*.hs test/data/ghc-mod-check/*.cabal test/data/ghc-mod-check/*.hs- test/data/ghc-mod-check/Data/*.hs- test/data/subdir1/subdir2/dummy- test/data/.cabal-sandbox/packages/00-index.tar+ test/data/ghc-mod-check/lib/Data/*.hs+ test/data/hlint/*.hs+ test/data/home-module-graph/cpp/*.hs+ test/data/home-module-graph/cycle/*.hs+ test/data/home-module-graph/errors/*.hs+ test/data/home-module-graph/indirect/*.hs+ test/data/home-module-graph/indirect-update/*.hs+ test/data/import-cycle/*.hs+ test/data/non-exported/*.hs+ test/data/pattern-synonyms/*.cabal+ test/data/pattern-synonyms/*.hs+ test/data/quasi-quotes/*.hs+ test/data/template-haskell/*.hs+ test/data/target/*.hs+ test/data/check-missing-warnings/*.hs+ test/data/custom-cradle/custom-cradle.cabal+ test/data/custom-cradle/ghc-mod.package-db-stack+ test/data/custom-cradle/package-db-a/.gitkeep+ test/data/custom-cradle/package-db-b/.gitkeep+ test/data/custom-cradle/package-db-c/.gitkeep+ test/data/cabal-preprocessors/*.cabal+ test/data/cabal-preprocessors/*.hs+ test/data/cabal-preprocessors/*.hsc+ test/data/file-mapping/*.hs+ test/data/file-mapping/preprocessor/*.hs+ test/data/file-mapping/lhs/*.lhs+ test/data/nice-qualification/*.hs+ test/data/stack-project/stack.yaml.in+ test/data/stack-project/new-template.cabal+ test/data/stack-project/*.hs+ test/data/stack-project/app/*.hs+ test/data/stack-project/src/*.hs+ test/data/stack-project/test/*.hs+ bench/data/simple-cabal/simple-cabal.cabal+ bench/data/simple-cabal/*.hs ++Custom-Setup+ Setup-Depends: base+ , Cabal >= 1.18 && < 1.25+ , containers+ , filepath+ , directory+ , process+ , template-haskell+ , transformers++ Library Default-Language: Haskell2010- GHC-Options: -Wall- Default-Extensions: ConstraintKinds, FlexibleContexts- Exposed-Modules: Language.Haskell.GhcMod- Language.Haskell.GhcMod.Internal- Other-Modules: Language.Haskell.GhcMod.Boot- Language.Haskell.GhcMod.Browse- Language.Haskell.GhcMod.Cabal16- Language.Haskell.GhcMod.Cabal18- Language.Haskell.GhcMod.Cabal21- Language.Haskell.GhcMod.CabalApi- Language.Haskell.GhcMod.CabalConfig- Language.Haskell.GhcMod.CaseSplit- Language.Haskell.GhcMod.Check- Language.Haskell.GhcMod.Convert- Language.Haskell.GhcMod.Cradle- Language.Haskell.GhcMod.Debug- Language.Haskell.GhcMod.Doc- Language.Haskell.GhcMod.DynFlags- Language.Haskell.GhcMod.Error- Language.Haskell.GhcMod.FillSig- Language.Haskell.GhcMod.Find- Language.Haskell.GhcMod.Flag- Language.Haskell.GhcMod.GHCApi- Language.Haskell.GhcMod.GHCChoice- Language.Haskell.GhcMod.Gap- Language.Haskell.GhcMod.GhcPkg- Language.Haskell.GhcMod.Info- Language.Haskell.GhcMod.Lang- Language.Haskell.GhcMod.Lint- Language.Haskell.GhcMod.Logger- Language.Haskell.GhcMod.Monad- Language.Haskell.GhcMod.Modules- Language.Haskell.GhcMod.PkgDoc- Language.Haskell.GhcMod.Read- Language.Haskell.GhcMod.SrcUtils- Language.Haskell.GhcMod.Target- Language.Haskell.GhcMod.Types- Language.Haskell.GhcMod.Utils- Build-Depends: base >= 4.0 && < 5+ GHC-Options: -Wall -fno-warn-deprecations+ Default-Extensions: ScopedTypeVariables, RecordWildCards, NamedFieldPuns,+ ConstraintKinds, FlexibleContexts,+ DataKinds, KindSignatures, TypeOperators, ViewPatterns+ HS-Source-Dirs: ., core, shared+ Exposed-Modules:+ GhcMod+ GhcMod.Exe.Boot+ GhcMod.Exe.Browse+ GhcMod.Exe.CaseSplit+ GhcMod.Exe.Check+ GhcMod.Exe.Debug+ GhcMod.Exe.FillSig+ GhcMod.Exe.Find+ GhcMod.Exe.Flag+ GhcMod.Exe.Info+ GhcMod.Exe.Internal+ GhcMod.Exe.Lang+ GhcMod.Exe.Lint+ GhcMod.Exe.Modules+ GhcMod.Exe.PkgDoc+ GhcMod.Exe.Test+ GhcMod.CabalHelper+ GhcMod.Caching+ GhcMod.Caching.Types+ GhcMod.Convert+ GhcMod.Cradle+ GhcMod.CustomPackageDb+ GhcMod.DebugLogger+ GhcMod.Doc+ GhcMod.DynFlags+ GhcMod.DynFlagsTH+ GhcMod.Error+ GhcMod.FileMapping+ GhcMod.Gap+ GhcMod.GhcPkg+ GhcMod.HomeModuleGraph+ GhcMod.LightGhc+ GhcMod.Logger+ GhcMod.Logging+ GhcMod.Monad+ GhcMod.Monad.Env+ GhcMod.Monad.Log+ GhcMod.Monad.Newtypes+ GhcMod.Monad.Orphans+ GhcMod.Monad.Out+ GhcMod.Monad.State+ GhcMod.Monad.Types+ GhcMod.Options.DocUtils+ GhcMod.Options.Help+ GhcMod.Options.Options+ GhcMod.Output+ GhcMod.PathsAndFiles+ GhcMod.Pretty+ GhcMod.Read+ GhcMod.SrcUtils+ GhcMod.Stack+ GhcMod.Target+ GhcMod.Types+ GhcMod.Utils+ GhcMod.World+ Other-Modules: Paths_ghc_mod+ Utils+ Data.Binary.Generic+ System.Directory.ModTime+ Build-Depends:+ -- See Note [GHC Boot libraries]+ binary+ , bytestring , containers , deepseq , directory , filepath- , ghc- , ghc-paths- , ghc-syb-utils- , hlint >= 1.8.61- , io-choice- , monad-journal >= 0.4+ , mtl , old-time , process- , syb+ , template-haskell , time , transformers- , transformers-base- , mtl >= 2.0- , monad-control- , split- , haskell-src-exts- , text- , djinn-ghc >= 0.0.2.2- if impl(ghc >= 7.8)- Build-Depends: Cabal >= 1.18- else- Build-Depends: convertible- , Cabal >= 1.10 && < 1.17- if impl(ghc <= 7.4.2)- -- Only used to constrain random to a version that still works with GHC 7.4- Build-Depends: random <= 1.0.1.1 + , base < 4.10 && >= 4.6.0.1+ , djinn-ghc < 0.1 && >= 0.0.2.2+ , extra < 1.6 && >= 1.4+ , fclabels < 2.1 && >= 2.0+ , ghc-paths < 0.2 && >= 0.1.0.9+ , ghc-syb-utils < 0.3 && >= 0.2.3+ , haskell-src-exts < 1.20 && >= 1.18+ , hlint < 2.1 && >= 2.0.8+ , monad-control < 1.1 && >= 1+ , monad-journal < 0.8 && >= 0.4+ , optparse-applicative < 0.14 && >= 0.13.0.0+ , pipes < 4.4 && >= 4.1+ , safe < 0.4 && >= 0.3.9+ , semigroups < 0.19 && >= 0.10.0+ , split < 0.3 && >= 0.2.2+ , syb < 0.7 && >= 0.5.1+ , temporary < 1.3 && >= 1.2.0.3+ , text < 1.3 && >= 1.2.1.3+ , transformers-base < 0.5 && >= 0.4.4++ , cabal-helper < 0.8 && >= 0.7.3.0+ , ghc < 8.2 && >= 7.6++ if impl(ghc >= 8.0)+ Build-Depends: ghc-boot+ if impl(ghc < 7.8)+ Build-Depends: convertible < 1.2 && >= 1.1.0.0+ Executable ghc-mod Default-Language: Haskell2010- Main-Is: GHCMod.hs+ Main-Is: GhcModMain.hs Other-Modules: Paths_ghc_mod- GHC-Options: -Wall+ , GhcMod.Exe.Options+ , GhcMod.Exe.Options.Commands+ , GhcMod.Exe.Version+ , GhcMod.Exe.Options.ShellParse+ GHC-Options: -Wall -fno-warn-deprecations -threaded Default-Extensions: ConstraintKinds, FlexibleContexts- HS-Source-Dirs: src- Build-Depends: base >= 4.0 && < 5- , directory+ HS-Source-Dirs: src, shared+ X-Internal: True+ Build-Depends:+ -- See Note [GHC Boot libraries]+ directory , filepath- , mtl >= 2.0- , ghc+ , mtl+ , process++ , base < 4.10 && >= 4.6.0.1+ , fclabels < 2.1 && >= 2.0+ , monad-control < 1.1 && >= 1+ , optparse-applicative < 0.14 && >= 0.13.0.0+ , semigroups < 0.19 && >= 0.10.0+ , split < 0.3 && >= 0.2.2++ , ghc < 8.2 && >= 7.6 , ghc-mod + Executable ghc-modi Default-Language: Haskell2010- Main-Is: GHCModi.hs+ Main-Is: GhcModi.hs Other-Modules: Paths_ghc_mod Utils- GHC-Options: -Wall -threaded+ System.Directory.ModTime+ GHC-Options: -Wall -threaded -fno-warn-deprecations+ if os(windows)+ Cpp-Options: -DWINDOWS Default-Extensions: ConstraintKinds, FlexibleContexts- HS-Source-Dirs: src- Build-Depends: base >= 4.0 && < 5- , async- , containers+ HS-Source-Dirs: ., src, shared+ Build-Depends:+ -- See Note [GHC Boot libraries]+ binary+ , deepseq , directory , filepath- , split- , ghc+ , old-time+ , process+ , time++ , base < 4.10 && >= 4.6.0.1+ , ghc-mod + Test-Suite doctest Type: exitcode-stdio-1.0 Default-Language: Haskell2010@@ -164,62 +274,116 @@ Ghc-Options: -Wall Default-Extensions: ConstraintKinds, FlexibleContexts Main-Is: doctests.hs- Build-Depends: base- , doctest >= 0.9.3+ Build-Depends: base < 4.10 && >= 4.6.0.1+ , doctest < 0.12 && >= 0.9.3 + Test-Suite spec Default-Language: Haskell2010- Default-Extensions: ConstraintKinds, FlexibleContexts+ Default-Extensions: ScopedTypeVariables, RecordWildCards, NamedFieldPuns,+ ConstraintKinds, FlexibleContexts,+ DataKinds, KindSignatures, TypeOperators, ViewPatterns Main-Is: Main.hs- Hs-Source-Dirs: test, .- Ghc-Options: -Wall+ Hs-Source-Dirs: test, src+ Ghc-Options: -Wall -fno-warn-deprecations -threaded Type: exitcode-stdio-1.0- Other-Modules: Dir- Spec+ Other-Modules: Paths_ghc_mod+ Dir+ TestUtils++-- $ ls test/*Spec.hs | sed 's_^.*/\(.*\)\.hs$_\1_' | sort BrowseSpec- CabalApiSpec+ CabalHelperSpec+ CaseSplitSpec CheckSpec+ CradleSpec+ CustomPackageDbSpec+ FileMappingSpec+ FindSpec FlagSpec+ GhcPkgSpec+ HomeModuleGraphSpec InfoSpec LangSpec LintSpec ListSpec MonadSpec- GhcPkgSpec- TestUtils- Build-Depends: base >= 4.0 && < 5- , containers- , deepseq+ PathsAndFilesSpec+ ShellParseSpec+ TargetSpec++ Build-Depends:+ -- See Note [GHC Boot libraries]+ containers , directory , filepath- , ghc- , ghc-paths- , ghc-syb-utils- , hlint >= 1.7.1- , io-choice- , monad-journal >= 0.4- , old-time+ , mtl , process- , syb- , time , transformers- , transformers-base- , mtl >= 2.0- , monad-control- , hspec >= 1.8.2- , split- , haskell-src-exts- , text- , djinn-ghc >= 0.0.2.2- if impl(ghc >= 7.8)- Build-Depends: Cabal >= 1.18- else- Build-Depends: convertible- , Cabal >= 1.10 && < 1.17- if impl(ghc < 7.6)- Build-Depends: executable-path- CPP-Options: -DSPEC=1 + , base < 4.10 && >= 4.6.0.1+ , fclabels < 2.1 && >= 2.0+ , hspec < 2.4 && >= 2.0.0+ , monad-journal < 0.8 && >= 0.4+ , split < 0.3 && >= 0.2.2+ , temporary < 1.3 && >= 1.2.0.3+++ if impl(ghc < 7.8)+ Build-Depends: convertible < 1.2 && >= 1.1.0.0+ if impl(ghc >= 8.0)+ Build-Depends: ghc-boot++ Build-Depends:+ cabal-helper < 0.8 && >= 0.7.1.0+ , ghc < 8.2 && >= 7.6+ , ghc-mod+++Test-Suite shelltest+ Default-Language: Haskell2010+ Main-Is: ShellTest.hs+ Hs-Source-Dirs: shelltest+ Type: exitcode-stdio-1.0+ Build-Tools: shelltest+ Build-Depends: base < 4.10 && >= 4.6.0.1+ , process < 1.5+ -- , shelltestrunner >= 1.3.5+ if !flag(shelltest)+ Buildable: False+++Benchmark criterion+ Type: exitcode-stdio-1.0+ Default-Language: Haskell2010+ Default-Extensions: ScopedTypeVariables, RecordWildCards, NamedFieldPuns,+ ConstraintKinds, FlexibleContexts,+ DataKinds, KindSignatures, TypeOperators, ViewPatterns+ HS-Source-Dirs: bench, test+ Main-Is: Bench.hs+ Build-Depends:+ -- See Note [GHC Boot libraries]+ directory+ , filepath++ , base < 4.10 && >= 4.6.0.1+ , criterion < 1.2 && >= 1.1.1.0+ , temporary < 1.3 && >= 1.2.0.3++ , ghc-mod+++Flag shelltest+ Description: Enable/disable shelltest test-suite+ Default: False+ Manual: True++ Source-Repository head Type: git- Location: git://github.com/kazu-yamamoto/ghc-mod.git+ Location: https://github.com/DanielG/ghc-mod.git++-- Note [GHC Boot libraries]+--+-- We don't give bounds to GHC boot libraries as our dependency on 'ghc' already+-- constrains these packages to the version that shipped with GHC.
@@ -0,0 +1,63 @@+-- ghc-mod: Happy Haskell Hacking+-- Copyright (C) 2015 Daniel Gröber <dxld ÄT darkboxed DOT org>+--+-- This program is free software: you can redistribute it and/or modify+-- it under the terms of the GNU Affero General Public License as published by+-- the Free Software Foundation, either version 3 of the License, or+-- (at your option) any later version.+--+-- This program is distributed in the hope that it will be useful,+-- but WITHOUT ANY WARRANTY; without even the implied warranty of+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+-- GNU Affero General Public License for more details.+--+-- You should have received a copy of the GNU Affero General Public License+-- along with this program. If not, see <http://www.gnu.org/licenses/>.+{-# LANGUAGE CPP, GeneralizedNewtypeDeriving #-}+module System.Directory.ModTime where++import Control.Applicative+import Control.DeepSeq+import Data.Binary+#if MIN_VERSION_directory(1,2,0)+import Data.Time (UTCTime(..), Day(..), getCurrentTime)+#else+import System.Time (ClockTime(..), getClockTime)+#endif+import System.Directory+import Prelude++#if MIN_VERSION_directory(1,2,0)++newtype ModTime = ModTime UTCTime+ deriving (Eq, Ord, NFData)+getCurrentModTime = ModTime <$> getCurrentTime++instance Binary ModTime where+ put (ModTime (UTCTime (ModifiedJulianDay day) difftime)) =+ put day >> put (toRational difftime)+ get =+ ModTime <$> (UTCTime <$> (ModifiedJulianDay <$> get) <*> (fromRational <$> get))++#else++newtype ModTime = ModTime ClockTime+ deriving (Eq, Ord)+getCurrentModTime = ModTime <$> getClockTime++instance Binary ModTime where+ put (ModTime (TOD s ps)) =+ put s >> put ps+ get =+ ModTime <$> (TOD <$> get <*> get)++instance NFData ModTime where+ rnf (ModTime (TOD s ps)) =+ s `seq` ps `seq` (ModTime $! TOD s ps) `seq` ()++#endif++getCurrentModTime :: IO ModTime++getModTime :: FilePath -> IO ModTime+getModTime f = ModTime <$> getModificationTime f
@@ -0,0 +1,26 @@+{-# LANGUAGE CPP #-}+module Utils where++import Control.Applicative+import Data.Traversable+import System.Directory+import System.Directory.ModTime++import Prelude++data TimedFile = TimedFile { tfPath :: FilePath, tfTime :: ModTime }+ deriving (Eq)++instance Ord TimedFile where+ compare (TimedFile _ a) (TimedFile _ b) = compare a b++timeFile :: FilePath -> IO TimedFile+timeFile f = TimedFile <$> pure f <*> getModTime f++mightExist :: FilePath -> IO (Maybe FilePath)+mightExist f = do+ exists <- doesFileExist f+ return $ if exists then (Just f) else (Nothing)++timeMaybe :: FilePath -> IO (Maybe TimedFile)+timeMaybe f = traverse timeFile =<< mightExist f
+ shelltest/ShellTest.hs view
@@ -0,0 +1,23 @@+#!/usr/bin/env runhaskell+-- ghc-mod: Happy Haskell Hacking+-- Copyright (C) 2017 Daniel Gröber <dxld ÄT darkboxed DOT org>+--+-- This program is free software: you can redistribute it and/or modify+-- it under the terms of the GNU Affero General Public License as published by+-- the Free Software Foundation, either version 3 of the License, or+-- (at your option) any later version.+--+-- This program is distributed in the hope that it will be useful,+-- but WITHOUT ANY WARRANTY; without even the implied warranty of+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+-- GNU Affero General Public License for more details.+--+-- You should have received a copy of the GNU Affero General Public License+-- along with this program. If not, see <http://www.gnu.org/licenses/>.++module Main where++import System.Exit+import System.Process++main = exitWith =<< rawSystem "shelltest" [ "--execdir", "shelltest/" ]
− src/GHCMod.hs
@@ -1,189 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}--module Main where--import Config (cProjectVersion)-import Control.Applicative ((<$>))-import Control.Exception (Exception, Handler(..), ErrorCall(..))-import CoreMonad (liftIO)-import qualified Control.Exception as E-import Data.Typeable (Typeable)-import Data.Version (showVersion)-import Language.Haskell.GhcMod-import Paths_ghc_mod-import System.Console.GetOpt (OptDescr(..), ArgDescr(..), ArgOrder(..))-import qualified System.Console.GetOpt as O-import System.Directory (doesFileExist)-import System.Environment (getArgs)-import System.Exit (exitFailure)-import System.IO (hPutStr, hPutStrLn, stdout, stderr, hSetEncoding, utf8)--------------------------------------------------------------------progVersion :: String-progVersion = "ghc-mod version " ++ showVersion version ++ " compiled by GHC " ++ cProjectVersion ++ "\n"--ghcOptHelp :: String-ghcOptHelp = " [-g GHC_opt1 -g GHC_opt2 ...] "--usage :: String-usage = progVersion- ++ "Usage:\n"- ++ "\t ghc-mod list " ++ ghcOptHelp ++ "[-l] [-d]\n"- ++ "\t ghc-mod lang [-l]\n"- ++ "\t ghc-mod flag [-l]\n"- ++ "\t ghc-mod browse " ++ ghcOptHelp ++ "[-l] [-o] [-d] [-q] [<package>:]<module> [[<package>:]<module> ...]\n"- ++ "\t ghc-mod check " ++ ghcOptHelp ++ "<HaskellFiles...>\n"- ++ "\t ghc-mod expand " ++ ghcOptHelp ++ "<HaskellFiles...>\n"- ++ "\t ghc-mod debug " ++ ghcOptHelp ++ "\n"- ++ "\t ghc-mod info " ++ ghcOptHelp ++ "<HaskellFile> <module> <expression>\n"- ++ "\t ghc-mod type " ++ ghcOptHelp ++ "<HaskellFile> <module> <line-no> <column-no>\n"- ++ "\t ghc-mod split " ++ ghcOptHelp ++ "<HaskellFile> <module> <line-no> <column-no>\n"- ++ "\t ghc-mod sig " ++ ghcOptHelp ++ "<HaskellFile> <module> <line-no> <column-no>\n"- ++ "\t ghc-mod refine " ++ ghcOptHelp ++ "<HaskellFile> <module> <line-no> <column-no> <expression>\n"- ++ "\t ghc-mod auto " ++ ghcOptHelp ++ "<HaskellFile> <module> <line-no> <column-no>\n"- ++ "\t ghc-mod find <symbol>\n"- ++ "\t ghc-mod lint [-h opt] <HaskellFile>\n"- ++ "\t ghc-mod root\n"- ++ "\t ghc-mod doc <module>\n"- ++ "\t ghc-mod boot\n"- ++ "\t ghc-mod version\n"- ++ "\t ghc-mod help\n"- ++ "\n"- ++ "<module> for \"info\" and \"type\" is not used, anything is OK.\n"- ++ "It is necessary to maintain backward compatibility.\n"--------------------------------------------------------------------argspec :: [OptDescr (Options -> Options)]-argspec =- let option s l udsc dsc = Option s l dsc udsc- reqArg udsc dsc = ReqArg dsc udsc- in- [ option "l" ["tolisp"] "print as a list of Lisp" $- NoArg $ \o -> o { outputStyle = LispStyle }-- , option "h" ["hlintOpt"] "hlint options" $- reqArg "hlintOpt" $ \h o -> o { hlintOpts = h : hlintOpts o }-- , option "g" ["ghcOpt"] "GHC options" $- reqArg "ghcOpt" $ \g o ->- o { ghcUserOptions = g : ghcUserOptions o }-- , option "v" ["verbose"] "verbose" $- NoArg $ \o -> o { ghcUserOptions = "-v" : ghcUserOptions o }-- , option "o" ["operators"] "print operators, too" $- NoArg $ \o -> o { operators = True }-- , option "d" ["detailed"] "print detailed info" $- NoArg $ \o -> o { detailed = True }-- , option "q" ["qualified"] "show qualified names" $- NoArg $ \o -> o { qualified = True }-- , option "b" ["boundary"] "specify line separator (default is Nul string)"$- reqArg "sep" $ \s o -> o { lineSeparator = LineSeparator s }- ]--parseArgs :: [OptDescr (Options -> Options)] -> [String] -> (Options, [String])-parseArgs spec argv- = case O.getOpt Permute spec argv of- (o,n,[] ) -> (foldr id defaultOptions o, n)- (_,_,errs) -> E.throw (CmdArg errs)--------------------------------------------------------------------data GHCModError = SafeList- | ArgumentsMismatch String- | NoSuchCommand String- | CmdArg [String]- | FileNotExist String deriving (Show, Typeable)--instance Exception GHCModError--------------------------------------------------------------------main :: IO ()-main = flip E.catches handlers $ do- hSetEncoding stdout utf8- args <- getArgs- let (opt,cmdArg) = parseArgs argspec args- let cmdArg0 = cmdArg !. 0- cmdArg1 = cmdArg !. 1- cmdArg3 = cmdArg !. 3- cmdArg4 = cmdArg !. 4- cmdArg5 = cmdArg !. 5- remainingArgs = tail cmdArg- nArgs :: Int -> a -> a- nArgs n f = if length remainingArgs == n- then f- else E.throw (ArgumentsMismatch cmdArg0)- (res, _) <- runGhcModT opt $ case cmdArg0 of- "list" -> modules- "lang" -> languages- "flag" -> flags- "browse" -> concat <$> mapM browse remainingArgs- "check" -> checkSyntax remainingArgs- "expand" -> expandTemplate remainingArgs- "debug" -> debugInfo- "info" -> nArgs 3 info cmdArg1 cmdArg3- "type" -> nArgs 4 $ types cmdArg1 (read cmdArg3) (read cmdArg4)- "split" -> nArgs 4 $ splits cmdArg1 (read cmdArg3) (read cmdArg4)- "sig" -> nArgs 4 $ sig cmdArg1 (read cmdArg3) (read cmdArg4)- "refine" -> nArgs 5 $ refine cmdArg1 (read cmdArg3) (read cmdArg4) cmdArg5- "auto" -> nArgs 4 $ auto cmdArg1 (read cmdArg3) (read cmdArg4)- "find" -> nArgs 1 $ findSymbol cmdArg1- "lint" -> nArgs 1 $ withFile lint cmdArg1- "root" -> rootInfo- "doc" -> nArgs 1 $ pkgDoc cmdArg1- "dumpsym" -> dumpSymbol- "boot" -> boot- "version" -> return progVersion- "help" -> return $ O.usageInfo usage argspec- cmd -> E.throw (NoSuchCommand cmd)-- case res of- Right s -> putStr s- Left (GMENoMsg) ->- hPutStrLn stderr "Unknown error"- Left (GMEString msg) ->- hPutStrLn stderr msg- Left (GMECabalConfigure msg) ->- hPutStrLn stderr $ "cabal configure failed: " ++ show msg- Left (GMECabalFlags msg) ->- hPutStrLn stderr $ "retrieval of the cabal configuration flags failed: " ++ show msg- Left (GMEProcess cmd msg) ->- hPutStrLn stderr $- "launching operating system process `"++c++"` failed: " ++ show msg- where c = unwords cmd-- where- handlers = [Handler (handleThenExit handler1), Handler (handleThenExit handler2)]- handleThenExit handler e = handler e >> exitFailure- handler1 :: ErrorCall -> IO ()- handler1 = print -- for debug- handler2 :: GHCModError -> IO ()- handler2 SafeList = printUsage- handler2 (ArgumentsMismatch cmd) = do- hPutStrLn stderr $ "\"" ++ cmd ++ "\": Arguments did not match"- printUsage- handler2 (NoSuchCommand cmd) = do- hPutStrLn stderr $ "\"" ++ cmd ++ "\" not supported"- printUsage- handler2 (CmdArg errs) = do- mapM_ (hPutStr stderr) errs- printUsage- handler2 (FileNotExist file) = do- hPutStrLn stderr $ "\"" ++ file ++ "\" not found"- printUsage- printUsage = hPutStrLn stderr $ '\n' : O.usageInfo usage argspec- withFile :: IOish m => (FilePath -> GhcModT m a) -> FilePath -> GhcModT m a- withFile cmd file = do- exist <- liftIO $ doesFileExist file- if exist- then cmd file- else E.throw (FileNotExist file)- xs !. idx- | length xs <= idx = E.throw SafeList- | otherwise = xs !! idx
− src/GHCModi.hs
@@ -1,323 +0,0 @@-{-# LANGUAGE ScopedTypeVariables, DeriveDataTypeable #-}---- Commands:--- check <file>--- find <symbol>--- info <file> <expr>--- type <file> <line> <column>--- lint [hlint options] <file>--- the format of hlint options is [String] because they may contain--- spaces and also <file> may contain spaces.--- boot--- browse [<package>:]<module>--- quit------ Session separators:--- OK -- success--- NG -- failure--module Main where--import Config (cProjectVersion)-import Control.Applicative ((<$>))-import Control.Concurrent.Async (Async, async, wait)-import Control.Exception (SomeException(..), Exception)-import qualified Control.Exception as E-import Control.Monad (when)-import CoreMonad (liftIO)-import Data.List (find, intercalate)-import Data.List.Split (splitOn)-import Data.Maybe (fromMaybe)-import Data.Set (Set)-import qualified Data.Set as S-import Data.Typeable (Typeable)-import Data.Version (showVersion)-import qualified GHC as G-import Language.Haskell.GhcMod-import Language.Haskell.GhcMod.Internal-import Paths_ghc_mod-import System.Console.GetOpt-import System.Directory (setCurrentDirectory)-import System.Environment (getArgs)-import System.IO (hFlush,stdout)-import System.Exit (ExitCode, exitFailure)--import Utils--------------------------------------------------------------------type Logger = IO String--------------------------------------------------------------------progVersion :: String-progVersion = "ghc-modi version " ++ showVersion version ++ " compiled by GHC " ++ cProjectVersion ++ "\n"--argspec :: [OptDescr (Options -> Options)]-argspec = [ Option "b" ["boundary"]- (ReqArg (\s opts -> opts { lineSeparator = LineSeparator s }) "sep")- "specify line separator (default is Nul string)"- , Option "l" ["tolisp"]- (NoArg (\opts -> opts { outputStyle = LispStyle }))- "print as a list of Lisp"- , Option "g" []- (ReqArg (\s opts -> opts { ghcUserOptions = s : ghcUserOptions opts }) "flag") "specify a ghc flag"- ]--usage :: String-usage = progVersion- ++ "Usage:\n"- ++ "\t ghc-modi [-l] [-b sep] [-g flag]\n"- ++ "\t ghc-modi version\n"- ++ "\t ghc-modi help\n"--parseArgs :: [OptDescr (Options -> Options)] -> [String] -> (Options, [String])-parseArgs spec argv- = case getOpt Permute spec argv of- (o,n,[] ) -> (foldr id defaultOptions o, n)- (_,_,errs) -> E.throw (CmdArg errs)--------------------------------------------------------------------data GHCModiError = CmdArg [String]- deriving (Show, Typeable)--instance Exception GHCModiError---------------------------------------------------------------------- Running two GHC monad threads disables the handling of--- C-c since installSignalHandlers is called twice, sigh.--main :: IO ()-main = E.handle cmdHandler $- go =<< parseArgs argspec <$> getArgs- where- cmdHandler (CmdArg _) = putStr $ usageInfo usage argspec- go (_,"help":_) = putStr $ usageInfo usage argspec- go (_,"version":_) = putStr progVersion- go (opt,_) = flip E.catches handlers $ do- cradle0 <- findCradle- let rootdir = cradleRootDir cradle0--- c = cradle0 { cradleCurrentDir = rootdir } TODO: ?????- setCurrentDirectory rootdir- symDb <- async $ runGhcModT opt loadSymbolDb- (res, _) <- runGhcModT opt $ loop S.empty symDb-- case res of- Right () -> return ()- Left (GMECabalConfigure msg) -> do- putStrLn $ notGood $ "cabal configure failed: " ++ show msg- exitFailure- Left e -> bug $ show e- where- -- this is just in case.- -- If an error is caught here, it is a bug of GhcMod library.- handlers = [ E.Handler (\(_ :: ExitCode) -> return ())- , E.Handler (\(SomeException e) -> bug $ show e) ]--bug :: String -> IO ()-bug msg = do- putStrLn $ notGood $ "BUG: " ++ msg- exitFailure--notGood :: String -> String-notGood msg = "NG " ++ escapeNewlines msg--escapeNewlines :: String -> String-escapeNewlines = replace "\n" "\\n" . replace "\\n" "\\\\n"--replace :: String -> String -> String -> String-replace needle replacement = intercalate replacement . splitOn needle--------------------------------------------------------------------loop :: IOish m => Set FilePath -> SymDbReq -> GhcModT m ()-loop set symDbReq = do- cmdArg <- liftIO getLine- let (cmd,arg') = break (== ' ') cmdArg- arg = dropWhile (== ' ') arg'- (ret,ok,set') <- case cmd of- "check" -> checkStx set arg- "find" -> findSym set arg symDbReq- "lint" -> lintStx set arg- "info" -> showInfo set arg- "type" -> showType set arg- "split" -> doSplit set arg- "sig" -> doSig set arg- "refine" -> doRefine set arg- "auto" -> doAuto set arg- "boot" -> bootIt set- "browse" -> browseIt set arg- "quit" -> return ("quit", False, set)- "" -> return ("quit", False, set)- _ -> return ([], True, set)- if ok then do- liftIO $ putStr ret- liftIO $ putStrLn "OK"- else do- liftIO $ putStrLn $ notGood ret- liftIO $ hFlush stdout- when ok $ loop set' symDbReq--------------------------------------------------------------------checkStx :: IOish m- => Set FilePath- -> FilePath- -> GhcModT m (String, Bool, Set FilePath)-checkStx set file = do- set' <- newFileSet set file- let files = S.toList set'- eret <- check files- case eret of- Right ret -> return (ret, True, set')- Left ret -> return (ret, True, set) -- fxime: set--newFileSet :: IOish m => Set FilePath -> FilePath -> GhcModT m (Set FilePath)-newFileSet set file = do- let set1- | S.member file set = set- | otherwise = S.insert file set- mx <- isSameMainFile file <$> getModSummaryForMain- return $ case mx of- Nothing -> set1- Just mainfile -> S.delete mainfile set1--getModSummaryForMain :: IOish m => GhcModT m (Maybe G.ModSummary)-getModSummaryForMain = find isMain <$> G.getModuleGraph- where- isMain m = G.moduleNameString (G.moduleName (G.ms_mod m)) == "Main"--isSameMainFile :: FilePath -> (Maybe G.ModSummary) -> Maybe FilePath-isSameMainFile _ Nothing = Nothing-isSameMainFile file (Just x)- | mainfile == file = Nothing- | otherwise = Just mainfile- where- mmainfile = G.ml_hs_file (G.ms_location x)- -- G.ms_hspp_file x is a temporary file with CPP.- -- this is a just fake.- mainfile = fromMaybe (G.ms_hspp_file x) mmainfile--------------------------------------------------------------------type SymDbReq = Async (Either GhcModError SymbolDb, GhcModLog)--findSym :: IOish m => Set FilePath -> String -> SymDbReq- -> GhcModT m (String, Bool, Set FilePath)-findSym set sym dbReq = do- db <- hoistGhcModT =<< liftIO (wait dbReq)- ret <- lookupSymbol sym db- return (ret, True, set)--lintStx :: IOish m => Set FilePath- -> FilePath- -> GhcModT m (String, Bool, Set FilePath)-lintStx set optFile = do- ret <- withOptions changeOpt $ lint file- return (ret, True, set)- where- (opts,file) = parseLintOptions optFile- hopts = if opts == "" then [] else read opts- changeOpt o = o { hlintOpts = hopts }---- |--- >>> parseLintOptions "[\"--ignore=Use camelCase\", \"--ignore=Eta reduce\"] file name"--- (["--ignore=Use camelCase", "--ignore=Eta reduce"], "file name")--- >>> parseLintOptions "file name"--- ([], "file name")-parseLintOptions :: String -> (String, String)-parseLintOptions optFile = case brk (== ']') (dropWhile (/= '[') optFile) of- ("","") -> ([], optFile)- (opt',file') -> (opt', dropWhile (== ' ') file')- where- brk _ [] = ([],[])- brk p (x:xs')- | p x = ([x],xs')- | otherwise = let (ys,zs) = brk p xs' in (x:ys,zs)--------------------------------------------------------------------showInfo :: IOish m- => Set FilePath- -> FilePath- -> GhcModT m (String, Bool, Set FilePath)-showInfo set fileArg = do- let [file, expr] = splitN 2 fileArg- set' <- newFileSet set file- ret <- info file expr- return (ret, True, set')--showType :: IOish m- => Set FilePath- -> FilePath- -> GhcModT m (String, Bool, Set FilePath)-showType set fileArg = do- let [file, line, column] = splitN 3 fileArg- set' <- newFileSet set file- ret <- types file (read line) (read column)- return (ret, True, set')--doSplit :: IOish m- => Set FilePath- -> FilePath- -> GhcModT m (String, Bool, Set FilePath)-doSplit set fileArg = do- let [file, line, column] = splitN 3 fileArg- set' <- newFileSet set file- ret <- splits file (read line) (read column)- return (ret, True, set')--doSig :: IOish m- => Set FilePath- -> FilePath- -> GhcModT m (String, Bool, Set FilePath)-doSig set fileArg = do- let [file, line, column] = splitN 3 fileArg- set' <- newFileSet set file- ret <- sig file (read line) (read column)- return (ret, True, set')--doRefine :: IOish m- => Set FilePath- -> FilePath- -> GhcModT m (String, Bool, Set FilePath)-doRefine set fileArg = do- let [file, line, column, expr] = splitN 4 fileArg- set' <- newFileSet set file- ret <- refine file (read line) (read column) expr- return (ret, True, set')--doAuto :: IOish m- => Set FilePath- -> FilePath- -> GhcModT m (String, Bool, Set FilePath)-doAuto set fileArg = do- let [file, line, column] = splitN 3 fileArg- set' <- newFileSet set file- ret <- auto file (read line) (read column)- return (ret, True, set')--------------------------------------------------------------------bootIt :: IOish m- => Set FilePath- -> GhcModT m (String, Bool, Set FilePath)-bootIt set = do- ret <- boot- return (ret, True, set)--browseIt :: IOish m- => Set FilePath- -> ModuleString- -> GhcModT m (String, Bool, Set FilePath)-browseIt set mdl = do- let (det,rest') = break (== ' ') mdl- rest = dropWhile (== ' ') rest'- ret <- if det == "-d"- then withOptions setDetailed (browse rest)- else browse mdl- return (ret, True, set)- where- setDetailed opt = opt { detailed = True }
+ src/GhcMod/Exe/Options.hs view
@@ -0,0 +1,72 @@+-- ghc-mod: Happy Haskell Hacking+-- Copyright (C) 2015 Nikolay Yakimov <root@livid.pp.ru>+--+-- This program is free software: you can redistribute it and/or modify+-- it under the terms of the GNU Affero General Public License as published by+-- the Free Software Foundation, either version 3 of the License, or+-- (at your option) any later version.+--+-- This program is distributed in the hope that it will be useful,+-- but WITHOUT ANY WARRANTY; without even the implied warranty of+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+-- GNU Affero General Public License for more details.+--+-- You should have received a copy of the GNU Affero General Public License+-- along with this program. If not, see <http://www.gnu.org/licenses/>.+{-# LANGUAGE OverloadedStrings #-}+{-# OPTIONS_GHC -fno-warn-unused-do-bind #-}++module GhcMod.Exe.Options (+ parseArgs,+ parseArgsInteractive,+ GhcModCommands(..)+) where++import Options.Applicative+import Options.Applicative.Types++import GhcMod.Exe.Options.Commands+import GhcMod.Exe.Options.ShellParse+import GhcMod.Exe.Version+import GhcMod.Options.DocUtils+import GhcMod.Options.Options+import GhcMod.Types++parseArgs :: IO (Options, GhcModCommands)+parseArgs =+ execParser opts+ where+ opts = info (argAndCmdSpec <**> helpVersion)+ $$ fullDesc+ <=> header "ghc-mod: Happy Haskell Hacking"++parseArgsInteractive :: String -> Either String GhcModCommands+parseArgsInteractive args =+ handle $ execParserPure (prefs idm) opts $ parseCmdLine args+ where+ opts = info interactiveCommandsSpec $$ fullDesc+ handle (Success a) = Right a+ handle (Failure failure) =+ Left $ fst $ renderFailure failure ""+ handle _ = Left "Completion invoked"++helpVersion :: Parser (a -> a)+helpVersion =+ helper+ <*> abortOption (InfoMsg ghcModVersion)+ $$ long "version"+ <=> help "Print the version of the program."+ <*> argument r+ $$ value id+ <=> metavar ""+ where+ r :: ReadM (a -> a)+ r = do+ v <- readerAsk+ case v of+ "help" -> readerAbort ShowHelpText+ "version" -> readerAbort $ InfoMsg ghcModVersion+ _ -> return id++argAndCmdSpec :: Parser (Options, GhcModCommands)+argAndCmdSpec = (,) <$> globalArgSpec <*> commandsSpec
+ src/GhcMod/Exe/Options/Commands.hs view
@@ -0,0 +1,301 @@+-- ghc-mod: Happy Haskell Hacking+-- Copyright (C) 2015 Nikolay Yakimov <root@livid.pp.ru>+--+-- This program is free software: you can redistribute it and/or modify+-- it under the terms of the GNU Affero General Public License as published by+-- the Free Software Foundation, either version 3 of the License, or+-- (at your option) any later version.+--+-- This program is distributed in the hope that it will be useful,+-- but WITHOUT ANY WARRANTY; without even the implied warranty of+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+-- GNU Affero General Public License for more details.+--+-- You should have received a copy of the GNU Affero General Public License+-- along with this program. If not, see <http://www.gnu.org/licenses/>.+{-# LANGUAGE OverloadedStrings #-}+{-# OPTIONS_GHC -fno-warn-unused-do-bind #-}++module GhcMod.Exe.Options.Commands where++import Data.Semigroup+import Options.Applicative+import Options.Applicative.Types+import Options.Applicative.Builder.Internal+import GhcMod.Types+import GhcMod.Read+import GhcMod.Options.DocUtils+import GhcMod.Options.Help++type Symbol = String+type Expr = String+type Module = String+type Line = Int+type Col = Int+type Point = (Line, Col)++data GhcModCommands =+ CmdLang+ | CmdFlag+ | CmdDebug+ | CmdBoot+ | CmdNukeCaches+ | CmdRoot+ | CmdLegacyInteractive+ | CmdModules Bool+ | CmdDumpSym+ | CmdFind Symbol+ | CmdDoc Module+ | CmdLint LintOpts FilePath+ | CmdBrowse BrowseOpts [Module]+ | CmdDebugComponent [String]+ | CmdCheck [FilePath]+ | CmdExpand [FilePath]+ | CmdInfo FilePath Symbol+ | CmdType Bool FilePath Point+ | CmdSplit FilePath Point+ | CmdSig FilePath Point+ | CmdAuto FilePath Point+ | CmdRefine FilePath Point Expr+ | CmdTest FilePath+ -- interactive-only commands+ | CmdMapFile FilePath+ | CmdUnmapFile FilePath+ | CmdQuit+ deriving (Show)++commandsSpec :: Parser GhcModCommands+commandsSpec =+ hsubparser commands++commands :: Mod CommandFields GhcModCommands+commands =+ command "lang"+ $$ info (pure CmdLang)+ $$ progDesc "List all known GHC language extensions"+ <> command "flag"+ $$ info (pure CmdFlag)+ $$ progDesc "List GHC -f<foo> flags"+ <> command "debug"+ $$ info (pure CmdDebug)+ $$ progDesc' $$$ do+ "Print debugging information. Please include"+ \\ "the output in any bug reports you submit"+ <> command "debug-component"+ $$ info debugComponentArgSpec+ $$ progDesc' $$$ do+ "Debugging information related to cabal component"+ \\ "resolution"+ <> command "boot"+ $$ info (pure CmdBoot)+ $$ progDesc "Internal command used by the emacs frontend"+ -- <> command "nuke-caches"+ -- $$ info (pure CmdNukeCaches) idm+ <> command "root"+ $$ info (pure CmdRoot)+ $$ progDesc'+ "Try to find the project directory."+ <=> desc $$$ do+ "For Cabal projects this is the"+ \\ "directory containing the cabal file, for projects"+ \\ "that use a cabal sandbox but have no cabal file"+ \\ "this is the directory containing the cabal.sandbox.config"+ \\ "file and otherwise this is the current directory"+ <> command "legacy-interactive"+ $$ info legacyInteractiveArgSpec+ $$ progDesc "ghc-modi compatibility mode"+ <> command "list"+ $$ info modulesArgSpec+ $$ progDesc "List all visible modules"+ <> command "modules"+ $$ info modulesArgSpec+ $$ progDesc "List all visible modules"+ <> command "dumpsym"+ $$ info (pure CmdDumpSym) idm+ <> command "find"+ $$ info findArgSpec+ $$ progDesc "List all modules that define SYMBOL"+ <> command "doc"+ $$ info docArgSpec+ $$ progDesc' $$$ do+ "Try finding the html documentation directory"+ \\ "for the given MODULE"+ <> command "lint"+ $$ info lintArgSpec+ $$ progDesc "Check files using `hlint'"+ <> command "browse"+ $$ info browseArgSpec+ $$ progDesc "List symbols in a module"+ <> command "check"+ $$ info checkArgSpec+ $$ progDesc' $$$ do+ "Load the given files using GHC and report errors/warnings,"+ \\ "but don't produce output files"+ <> command "expand"+ $$ info expandArgSpec+ $$ progDesc "Like `check' but also pass `-ddump-splices' to GHC"+ <> command "info"+ $$ info infoArgSpec+ $$ progDesc' $$$ do+ "Look up an identifier in the context"+ \\ "of FILE (like ghci's `:info')"+ <> command "type"+ $$ info typeArgSpec+ $$ progDesc "Get the type of the expression under (LINE,COL)"+ <> command "split"+ $$ info splitArgSpec+ $$ progDesc+ "Split a function case by examining a type's constructors"+ <=> desc $$$ do+ "For example given the following code snippet:"+ code $ do+ "f :: [a] -> a"+ "f x = _body"+ "would be replaced by:"+ code $ do+ "f :: [a] -> a"+ "f [] = _body"+ "f (x:xs) = _body"+ "(See https://github.com/DanielG/ghc-mod/pull/274)"+ <> command "sig"+ $$ info sigArgSpec+ $$ progDesc "Generate initial code given a signature"+ <=> desc $$$ do+ "For example when (LINE,COL) is on the"+ \\ "signature in the following code snippet:"+ code "func :: [a] -> Maybe b -> (a -> b) -> (a,b)"+ "ghc-mod would add the following on the next line:"+ code "func x y z f = _func_body"+ "(See: https://github.com/DanielG/ghc-mod/pull/274)"+ <> command "auto"+ $$ info autoArgSpec+ $$ progDesc "Try to automatically fill the contents of a hole"+ <> command "refine"+ $$ info refineArgSpec+ $$ progDesc "Refine the typed hole at (LINE,COL) given EXPR"+ <=> desc $$$ do+ "For example if EXPR is `filter', which has type"+ \\ "`(a -> Bool) -> [a] -> [a]' and (LINE,COL) is on"+ \\ " the hole `_body' in the following code snippet:"+ code $ do+ "filterNothing :: [Maybe a] -> [a]"+ "filterNothing xs = _body"+ "ghc-mod changes the code to get a value of type"+ \\ " `[a]', which results in:"+ code "filterNothing xs = filter _body_1 _body_2"+ "(See also: https://github.com/DanielG/ghc-mod/issues/311)"+ <> command "test"+ $$ info (CmdTest <$> strArg "FILE")+ $$ progDesc ""+++interactiveCommandsSpec :: Parser GhcModCommands+interactiveCommandsSpec =+ hsubparser'+ $ commands+ <> command "map-file"+ $$ info mapArgSpec+ $$ progDesc "tells ghc-modi to read `file.hs` source from stdin"+ <=> desc $$$ do+ "Works the same as second form of"+ \\ "`--map-file` CLI option."+ <> command "unmap-file"+ $$ info unmapArgSpec+ $$ progDesc' $$$ do+ "unloads previously mapped file,"+ \\ "so that it's no longer mapped."+ <=> desc $$$ do+ "`file.hs` can be full path or relative"+ \\ "to project root, either will work."+ <> command "quit"+ $$ info (pure CmdQuit)+ $$ progDesc "Exit interactive mode"+ <> command ""+ $$ info (pure CmdQuit) idm++strArg :: String -> Parser String+strArg = argument str . metavar++filesArgsSpec :: Parser ([String] -> b) -> Parser b+filesArgsSpec x = x <*> some (strArg "FILES..")++locArgSpec :: Parser (String -> (Int, Int) -> b) -> Parser b+locArgSpec x = x+ <*> strArg "FILE"+ <*> ( (,)+ <$> argument int (metavar "LINE")+ <*> argument int (metavar "COL")+ )++modulesArgSpec, docArgSpec, findArgSpec,+ lintArgSpec, browseArgSpec, checkArgSpec, expandArgSpec,+ infoArgSpec, typeArgSpec, autoArgSpec, splitArgSpec,+ sigArgSpec, refineArgSpec, debugComponentArgSpec,+ mapArgSpec, unmapArgSpec, legacyInteractiveArgSpec :: Parser GhcModCommands++modulesArgSpec = CmdModules+ <$> switch+ $$ long "detailed"+ <=> short 'd'+ <=> help "Print package modules belong to"+findArgSpec = CmdFind <$> strArg "SYMBOL"+docArgSpec = CmdDoc <$> strArg "MODULE"+lintArgSpec = CmdLint+ <$> LintOpts <$$> many $$ strOption+ $$ long "hlintOpt"+ <=> short 'h'+ <=> help "Option to be passed to hlint"+ <*> strArg "FILE"+browseArgSpec = CmdBrowse+ <$> (BrowseOpts+ <$> switch+ $$ long "operators"+ <=> short 'o'+ <=> help "Also print operators"+ <*> switch+ $$ long "detailed"+ <=> short 'd'+ <=> help "Print symbols with accompanying signature"+ <*> switch+ $$ long "parents"+ <=> short 'p'+ <=> help "Print symbols parents"+ <*> switch+ $$ long "qualified"+ <=> short 'q'+ <=> help "Qualify symbols"+ )+ <*> some (strArg "MODULE")+debugComponentArgSpec = filesArgsSpec (pure CmdDebugComponent)+checkArgSpec = filesArgsSpec (pure CmdCheck)+expandArgSpec = filesArgsSpec (pure CmdExpand)+infoArgSpec = CmdInfo+ <$> strArg "FILE"+ <*> strArg "SYMBOL"+typeArgSpec = locArgSpec $ CmdType <$>+ switch+ $$ long "constraints"+ <=> short 'c'+ <=> help "Include constraints into type signature"+autoArgSpec = locArgSpec (pure CmdAuto)+splitArgSpec = locArgSpec (pure CmdSplit)+sigArgSpec = locArgSpec (pure CmdSig)+refineArgSpec = locArgSpec (pure CmdRefine) <*> strArg "SYMBOL"+mapArgSpec = CmdMapFile <$> strArg "FILE"+unmapArgSpec = CmdUnmapFile <$> strArg "FILE"+legacyInteractiveArgSpec = const CmdLegacyInteractive <$>+ optional interactiveCommandsSpec++hsubparser' :: Mod CommandFields a -> Parser a+hsubparser' m = mkParser d g rdr+ where+ Mod _ d g = m `mappend` metavar ""+ (ms, cmds, subs) = mkCommand m+ rdr = CmdReader ms cmds (fmap add_helper . subs)+ add_helper pinfo = pinfo+ { infoParser = infoParser pinfo <**> helper }++int :: ReadM Int+int = do+ v <- readerAsk+ maybe (readerError $ "Not a number \"" ++ v ++ "\"") return $ readMaybe v
+ src/GhcMod/Exe/Options/ShellParse.hs view
@@ -0,0 +1,44 @@+-- ghc-mod: Happy Haskell Hacking+-- Copyright (C) 2015 Nikolay Yakimov <root@livid.pp.ru>+--+-- This program is free software: you can redistribute it and/or modify+-- it under the terms of the GNU Affero General Public License as published by+-- the Free Software Foundation, either version 3 of the License, or+-- (at your option) any later version.+--+-- This program is distributed in the hope that it will be useful,+-- but WITHOUT ANY WARRANTY; without even the implied warranty of+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+-- GNU Affero General Public License for more details.+--+-- You should have received a copy of the GNU Affero General Public License+-- along with this program. If not, see <http://www.gnu.org/licenses/>.+module GhcMod.Exe.Options.ShellParse (parseCmdLine) where++import Data.Char+import Data.List++go :: String -> String -> [String] -> Bool -> [String]+-- result+go [] curarg accargs _ = reverse $ reverse curarg : accargs+go (c:cl) curarg accargs quotes+ -- open quotes+ | c == '\STX', not quotes+ = go cl curarg accargs True+ -- close quotes+ | c == '\ETX', quotes+ = go cl curarg accargs False+ -- space separates arguments outside quotes+ | isSpace c, not quotes+ = if null curarg+ then go cl curarg accargs quotes+ else go cl [] (reverse curarg : accargs) quotes+ -- general character+ | otherwise = go cl (c:curarg) accargs quotes++parseCmdLine :: String -> [String]+parseCmdLine comline'+ | Just comline <- stripPrefix "ascii-escape " $ dropWhile isSpace comline'+ = go (dropWhile isSpace comline) [] [] False+parseCmdLine [] = [""]+parseCmdLine comline = words comline
+ src/GhcMod/Exe/Version.hs view
@@ -0,0 +1,32 @@+-- ghc-mod: Happy Haskell Hacking+-- Copyright (C) 2015 Nikolay Yakimov <root@livid.pp.ru>+--+-- This program is free software: you can redistribute it and/or modify+-- it under the terms of the GNU Affero General Public License as published by+-- the Free Software Foundation, either version 3 of the License, or+-- (at your option) any later version.+--+-- This program is distributed in the hope that it will be useful,+-- but WITHOUT ANY WARRANTY; without even the implied warranty of+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+-- GNU Affero General Public License for more details.+--+-- You should have received a copy of the GNU Affero General Public License+-- along with this program. If not, see <http://www.gnu.org/licenses/>.++module GhcMod.Exe.Version where++import Paths_ghc_mod+import Data.Version (showVersion)+import Config (cProjectVersion)++progVersion :: String -> String+progVersion pf =+ "ghc-mod"++pf++" version " ++ showVersion version ++ " compiled by GHC "+ ++ cProjectVersion++ghcModVersion :: String+ghcModVersion = progVersion ""++ghcModiVersion :: String+ghcModiVersion = progVersion "i"
+ src/GhcModMain.hs view
@@ -0,0 +1,184 @@+{-# LANGUAGE ScopedTypeVariables, DeriveDataTypeable #-}++module Main where++import Control.Applicative+import Control.Monad+import Data.Typeable (Typeable)+import Data.List+import Data.List.Split+import GhcMod.Pretty+import System.FilePath ((</>))+import System.Directory (setCurrentDirectory, getAppUserDataDirectory,+ removeDirectoryRecursive)+import System.IO+import System.Exit+import Prelude++import GhcMod+import GhcMod.Exe.Find+import GhcMod.Exe.Options+import GhcMod.Exe.Internal hiding (MonadIO,liftIO)+import GhcMod.Monad+import GhcMod.Types++import Exception++handler :: IOish m => GhcModT m a -> GhcModT m a+handler = flip gcatches+ [ GHandler $ \(e :: ExitCode) -> throw e+ , GHandler $ \(SomeException e) -> exitError $ "ghc-mod: " ++ show e+ ]++main :: IO ()+main =+ parseArgs >>= \res@(globalOptions, _) -> do+ enc <- mkTextEncoding $ optEncoding globalOptions+ hSetEncoding stdout enc+ hSetEncoding stderr enc+ hSetEncoding stdin enc+ catches (progMain res) [+ Handler $ \(e :: GhcModError) ->+ runGmOutT globalOptions $ exitError $ renderGm (gmeDoc e)+ ]++progMain :: (Options, GhcModCommands) -> IO ()+progMain (globalOptions, commands) = runGmOutT globalOptions $+ wrapGhcCommands globalOptions commands++-- ghc-modi+legacyInteractive :: IOish m => GhcModT m ()+legacyInteractive = do+ prepareCabalHelper+ asyncSymbolDb <- newAsyncSymbolDb+ world <- getCurrentWorld+ legacyInteractiveLoop asyncSymbolDb world++legacyInteractiveLoop :: IOish m => AsyncSymbolDb -> World -> GhcModT m ()+legacyInteractiveLoop asyncSymbolDb world = do+ liftIO . setCurrentDirectory =<< cradleRootDir <$> cradle++ -- blocking+ cmdArg <- liftIO getLine++ -- after blocking, we need to see if the world has changed.++ changed <- didWorldChange world++ world' <- if changed+ then getCurrentWorld -- TODO: gah, we're hitting the fs twice+ else return world++ when changed dropSession++ res <- flip gcatches interactiveHandlers $ do+ pargs <- either (throw . InvalidCommandLine . Right) return+ $ parseArgsInteractive cmdArg+ case pargs of+ CmdFind symbol ->+ lookupSymbol symbol =<< getAsyncSymbolDb asyncSymbolDb+ -- other commands are handled here+ x -> ghcCommands x++ gmPutStr res >> gmPutStrLn "OK" >> liftIO (hFlush stdout)+ legacyInteractiveLoop asyncSymbolDb world'+ where+ interactiveHandlers =+ [ GHandler $ \(e :: ExitCode) -> throw e+ , GHandler $ \(InvalidCommandLine e) -> do+ let err = notGood $ either ("Invalid command line: "++) Prelude.id e+ liftIO $ do+ putStr err+ exitFailure+ , GHandler $ \(SomeException e) -> gmErrStrLn (show e) >> return ""+ ]+ notGood msg = "NG " ++ escapeNewlines msg+ escapeNewlines = replace "\n" "\\n" . replace "\\n" "\\\\n"+ replace needle replacement = intercalate replacement . splitOn needle++getFileSourceFromStdin :: IO String+getFileSourceFromStdin = do+ linesIn <- readStdin'+ return (intercalate "\n" linesIn)+ where+ readStdin' = do+ x <- getLine+ if x/="\EOT"+ then fmap (x:) readStdin'+ else return []++wrapGhcCommands :: (IOish m, GmOut m) => Options -> GhcModCommands -> m ()+wrapGhcCommands opts cmd =+ handleGmError $ runGhcModT opts $ handler $ do+ forM_ (reverse $ optFileMappings opts) $+ uncurry loadMMappedFiles++ gmPutStr =<< ghcCommands cmd+ where+ handleGmError action = do+ (e, _l) <- liftIO . evaluate =<< action+ case e of+ Right _ ->+ return ()+ Left ed ->+ exitError $ renderGm (gmeDoc ed)++ loadMMappedFiles from (Just to) = loadMappedFile from to+ loadMMappedFiles from (Nothing) = do+ src <- liftIO getFileSourceFromStdin+ loadMappedFileSource from src+++ghcCommands :: IOish m => GhcModCommands -> GhcModT m String+-- ghcCommands cmd = action args+ghcCommands (CmdLang) = languages+ghcCommands (CmdFlag) = flags+ghcCommands (CmdDebug) = debugInfo+ghcCommands (CmdDebugComponent ts) = componentInfo ts+ghcCommands (CmdBoot) = boot+-- ghcCommands (CmdNukeCaches) = nukeCaches >> return ""+ghcCommands (CmdRoot) = rootInfo+ghcCommands (CmdLegacyInteractive) = legacyInteractive >> return ""+ghcCommands (CmdModules detail) = modules detail+ghcCommands (CmdDumpSym) = dumpSymbol >> return ""+ghcCommands (CmdFind symb) = findSymbol symb+ghcCommands (CmdDoc m) = pkgDoc m+ghcCommands (CmdLint opts file) = lint opts file+ghcCommands (CmdBrowse opts ms) = concat <$> browse opts `mapM` ms+ghcCommands (CmdCheck files) = checkSyntax files+ghcCommands (CmdExpand files) = expandTemplate files+ghcCommands (CmdInfo file symb) = info file $ Expression symb+ghcCommands (CmdType wCon file (line, col)) = types wCon file line col+ghcCommands (CmdSplit file (line, col)) = splits file line col+ghcCommands (CmdSig file (line, col)) = sig file line col+ghcCommands (CmdAuto file (line, col)) = auto file line col+ghcCommands (CmdRefine file (line, col) expr) = refine file line col $ Expression expr+-- interactive-only commands+ghcCommands (CmdMapFile f) =+ liftIO getFileSourceFromStdin+ >>= loadMappedFileSource f+ >> return ""+ghcCommands (CmdUnmapFile f) = unloadMappedFile f >> return ""+ghcCommands (CmdQuit) = liftIO exitSuccess+ghcCommands (CmdTest file) = test file+ghcCommands cmd = throw $ InvalidCommandLine $ Left $ show cmd++newtype InvalidCommandLine = InvalidCommandLine (Either String String)+ deriving (Show, Typeable)+instance Exception InvalidCommandLine++exitError :: (MonadIO m, GmOut m) => String -> m a+exitError msg = gmErrStrLn (dropWhileEnd (=='\n') msg) >> liftIO exitFailure++nukeCaches :: IOish m => GhcModT m ()+nukeCaches = do+ chdir <- liftIO $ (</> "cabal-helper") <$> getAppUserDataDirectory "ghc-mod"+ c <- cradle++ when (isCabalHelperProject $ cradleProject c) $ do+ let root = cradleRootDir c+ let dist = cradleDistDir c+ liftIO $ (trySome . removeDirectoryRecursive) `mapM_` [chdir, root </> dist]++trySome :: IO a -> IO (Either SomeException a)+trySome = try
+ src/GhcModi.hs view
@@ -0,0 +1,55 @@+{-# LANGUAGE ScopedTypeVariables #-}++-- | WARNING+-- This program is deprecated, use `ghc-mod legacy-interactive` instead.++module Main where++import Control.Applicative+import Control.Monad+import Control.Exception+import Data.Version+import Data.Maybe+import System.IO+import System.Exit+import System.Process+import System.FilePath+import System.Environment+import Paths_ghc_mod+import Utils+import Prelude++main :: IO ()+main = do+ hPutStrLn stderr $+ "Warning: ghc-modi is deprecated please use 'ghc-mod legacy-interactive' instead"++ args <- getArgs+ bindir <- getBinDir+ let installedExe = bindir </> "ghc-mod"+ mexe <- mplus <$> mightExist installedExe <*> pathExe+ case mexe of+ Nothing -> do+ hPutStrLn stderr $+ "ghc-modi: Could not find '"++installedExe++"', check your installation!"+ exitWith $ ExitFailure 1++ Just exe -> do+ (_, _, _, h) <-+ createProcess $ proc exe $ ["legacy-interactive"] ++ args+ exitWith =<< waitForProcess h++pathExe :: IO (Maybe String)+pathExe = do+ ev <- try $ words <$> readProcess "ghc-mod" ["--version"] ""+ let mexe = case ev of+ Left (SomeException _) -> Nothing+ Right ["ghc-mod", "version", ver+ , "compiled", "by", "GHC", _]+ | showVersion version == ver -> do+ Just "ghc-mod"+ Right _ -> Nothing++ when (isNothing mexe) $+ hPutStrLn stderr "ghc-modi: ghc-mod executable on PATH has different version, check your installation!"+ return mexe
− src/Utils.hs
@@ -1,27 +0,0 @@-module Utils where---- |------ >>> split "foo bar baz"--- ["foo","bar baz"]--- >>> split "foo bar baz"--- ["foo","bar baz"]-split :: String -> [String]-split xs = [ys, dropWhile isSpace zs]- where- isSpace = (== ' ')- (ys,zs) = break isSpace xs---- |------ >>> splitN 0 "foo bar baz"--- ["foo","bar baz"]--- >>> splitN 2 "foo bar baz"--- ["foo","bar baz"]--- >>> splitN 3 "foo bar baz"--- ["foo","bar","baz"]-splitN :: Int -> String -> [String]-splitN n xs- | n <= 2 = split xs- | otherwise = let [ys,zs] = split xs- in ys : splitN (n - 1) zs
test/BrowseSpec.hs view
@@ -1,8 +1,9 @@ module BrowseSpec where import Control.Applicative-import Language.Haskell.GhcMod+import GhcMod import Test.Hspec+import Prelude import TestUtils import Dir@@ -11,22 +12,23 @@ spec = do describe "browse Data.Map" $ do it "contains at least `differenceWithKey'" $ do- syms <- runD $ lines <$> browse "Data.Map"+ syms <- runD $ lines <$> browse defaultBrowseOpts "Data.Map" syms `shouldContain` ["differenceWithKey"] describe "browse -d Data.Either" $ do it "contains functions (e.g. `either') including their type signature" $ do- syms <- run defaultOptions { detailed = True }- $ lines <$> browse "Data.Either"+ syms <- runD+ $ lines <$> browse defaultBrowseOpts{ optBrowseDetailed = True } "Data.Either" syms `shouldContain` ["either :: (a -> c) -> (b -> c) -> Either a b -> c"] it "contains type constructors (e.g. `Left') including their type signature" $ do- syms <- run defaultOptions { detailed = True}- $ lines <$> browse "Data.Either"+ syms <- runD+ $ lines <$> browse defaultBrowseOpts{ optBrowseDetailed = True } "Data.Either" syms `shouldContain` ["Left :: a -> Either a b"] describe "`browse' in a project directory" $ do- it "lists symbols defined in a a local module (e.g. `Baz.baz)" $ do- withDirectory_ "test/data" $ do- syms <- runID $ lines <$> browse "Baz"- syms `shouldContain` ["baz"]+ it "can list symbols defined in a a local module" $ do+ withDirectory_ "test/data/ghc-mod-check/" $ do+ syms <- runD $ lines <$> browse defaultBrowseOpts "Data.Foo"+ syms `shouldContain` ["foo"]+ syms `shouldContain` ["fibonacci"]
− test/CabalApiSpec.hs
@@ -1,77 +0,0 @@-{-# LANGUAGE ScopedTypeVariables #-}--module CabalApiSpec where--import Control.Applicative-import Data.Maybe-import Language.Haskell.GhcMod.CabalApi-import Language.Haskell.GhcMod.Cradle-import Language.Haskell.GhcMod.Types-import Test.Hspec-import System.Directory-import System.FilePath-import System.Process (readProcess)--import Dir-import TestUtils--import Config (cProjectVersionInt) -- ghc version--ghcVersion :: Int-ghcVersion = read cProjectVersionInt--spec :: Spec-spec = do- describe "parseCabalFile" $ do- it "throws an exception if the cabal file is broken" $ do- shouldReturnError $ do- withDirectory_ "test/data/broken-cabal" $ do- crdl <- findCradle- runD' $ parseCabalFile crdl "broken.cabal"--- describe "getCompilerOptions" $ do- it "gets necessary CompilerOptions" $ do- cwd <- getCurrentDirectory- withDirectory "test/data/subdir1/subdir2" $ \dir -> do- crdl <- findCradle- pkgDesc <- runD $ parseCabalFile crdl $ fromJust $ cradleCabalFile crdl- res <- runD $ getCompilerOptions [] crdl pkgDesc- let res' = res {- ghcOptions = ghcOptions res- , includeDirs = map (toRelativeDir dir) (includeDirs res)- }- if ghcVersion < 706- then ghcOptions res' `shouldContain` ["-global-package-conf", "-no-user-package-conf","-package-conf",cwd </> "test/data/.cabal-sandbox/i386-osx-ghc-7.6.3-packages.conf.d","-XHaskell98"]- else ghcOptions res' `shouldContain` ["-global-package-db", "-no-user-package-db","-package-db",cwd </> "test/data/.cabal-sandbox/i386-osx-ghc-7.6.3-packages.conf.d","-XHaskell98"]- includeDirs res' `shouldBe` ["test/data","test/data/dist/build","test/data/dist/build/autogen","test/data/subdir1/subdir2","test/data/test"]- (pkgName `map` depPackages res') `shouldContain` ["Cabal"]--- describe "cabalDependPackages" $ do- it "extracts dependent packages" $ do- crdl <- findCradle' "test/data/"- pkgs <- cabalDependPackages . cabalAllBuildInfo <$> runD (parseCabalFile crdl "test/data/cabalapi.cabal")- pkgs `shouldBe` ["Cabal","base","template-haskell"]- it "uses non default flags" $ do- withDirectory_ "test/data/cabal-flags" $ do- crdl <- findCradle- _ <- readProcess "cabal" ["configure", "-ftest-flag"] ""- pkgs <- cabalDependPackages . cabalAllBuildInfo <$> runD (parseCabalFile crdl "cabal-flags.cabal")- pkgs `shouldBe` ["Cabal","base"]-- describe "cabalSourceDirs" $ do- it "extracts all hs-source-dirs" $ do- crdl <- findCradle' "test/data/check-test-subdir"- dirs <- cabalSourceDirs . cabalAllBuildInfo <$> runD (parseCabalFile crdl "test/data/check-test-subdir/check-test-subdir.cabal")- dirs `shouldBe` ["src", "test"]- it "extracts all hs-source-dirs including \".\"" $ do- crdl <- findCradle' "test/data/"- dirs <- cabalSourceDirs . cabalAllBuildInfo <$> runD (parseCabalFile crdl "test/data/cabalapi.cabal")- dirs `shouldBe` [".", "test"]-- describe "cabalAllBuildInfo" $ do- it "extracts build info" $ do- crdl <- findCradle' "test/data/"- info <- cabalAllBuildInfo <$> runD (parseCabalFile crdl "test/data/cabalapi.cabal")- show info `shouldBe` "[BuildInfo {buildable = True, buildTools = [], cppOptions = [], ccOptions = [], ldOptions = [], pkgconfigDepends = [], frameworks = [], cSources = [], hsSourceDirs = [\".\"], otherModules = [ModuleName [\"Browse\"],ModuleName [\"CabalApi\"],ModuleName [\"Cabal\"],ModuleName [\"CabalDev\"],ModuleName [\"Check\"],ModuleName [\"ErrMsg\"],ModuleName [\"Flag\"],ModuleName [\"GHCApi\"],ModuleName [\"GHCChoice\"],ModuleName [\"Gap\"],ModuleName [\"Info\"],ModuleName [\"Lang\"],ModuleName [\"Lint\"],ModuleName [\"List\"],ModuleName [\"Paths_ghc_mod\"],ModuleName [\"Types\"]], defaultLanguage = Nothing, otherLanguages = [], defaultExtensions = [], otherExtensions = [], oldExtensions = [], extraLibs = [], extraLibDirs = [], includeDirs = [], includes = [], installIncludes = [], options = [(GHC,[\"-Wall\"])], ghcProfOptions = [], ghcSharedOptions = [], customFieldsBI = [], targetBuildDepends = [Dependency (PackageName \"Cabal\") (UnionVersionRanges (ThisVersion (Version {versionBranch = [1,10], versionTags = []})) (LaterVersion (Version {versionBranch = [1,10], versionTags = []}))),Dependency (PackageName \"base\") (IntersectVersionRanges (UnionVersionRanges (ThisVersion (Version {versionBranch = [4,0], versionTags = []})) (LaterVersion (Version {versionBranch = [4,0], versionTags = []}))) (EarlierVersion (Version {versionBranch = [5], versionTags = []}))),Dependency (PackageName \"template-haskell\") AnyVersion]},BuildInfo {buildable = True, buildTools = [], cppOptions = [], ccOptions = [], ldOptions = [], pkgconfigDepends = [], frameworks = [], cSources = [], hsSourceDirs = [\"test\",\".\"], otherModules = [ModuleName [\"Expectation\"],ModuleName [\"BrowseSpec\"],ModuleName [\"CabalApiSpec\"],ModuleName [\"FlagSpec\"],ModuleName [\"LangSpec\"],ModuleName [\"LintSpec\"],ModuleName [\"ListSpec\"]], defaultLanguage = Nothing, otherLanguages = [], defaultExtensions = [], otherExtensions = [], oldExtensions = [], extraLibs = [], extraLibDirs = [], includeDirs = [], includes = [], installIncludes = [], options = [], ghcProfOptions = [], ghcSharedOptions = [], customFieldsBI = [], targetBuildDepends = [Dependency (PackageName \"Cabal\") (UnionVersionRanges (ThisVersion (Version {versionBranch = [1,10], versionTags = []})) (LaterVersion (Version {versionBranch = [1,10], versionTags = []}))),Dependency (PackageName \"base\") (IntersectVersionRanges (UnionVersionRanges (ThisVersion (Version {versionBranch = [4,0], versionTags = []})) (LaterVersion (Version {versionBranch = [4,0], versionTags = []}))) (EarlierVersion (Version {versionBranch = [5], versionTags = []})))]}]"
+ test/CabalHelperSpec.hs view
@@ -0,0 +1,101 @@+{-# LANGUAGE CPP #-}+module CabalHelperSpec where++import Control.Arrow+import Control.Applicative+import Distribution.Helper+import GhcMod.CabalHelper+import GhcMod.PathsAndFiles+import GhcMod.Error+import Test.Hspec+import System.Directory+import System.FilePath+import System.Process+import Prelude++import Dir+import TestUtils+import Data.List++import Config (cProjectVersionInt)++ghcVersion :: Int+ghcVersion = read cProjectVersionInt++gmeProcessException :: GhcModError -> Bool+gmeProcessException GMEProcess {} = True+gmeProcessException _ = False++pkgOptions :: [String] -> [String]+pkgOptions [] = []+pkgOptions (_:[]) = []+pkgOptions (x:y:xs) | x == "-package-id" = [name y] ++ pkgOptions xs+ | otherwise = pkgOptions (y:xs)+ where+ stripDash s = maybe s id $ (flip drop s . (+1) <$> findIndex (=='-') s)+#if __GLASGOW_HASKELL__ >= 800+ name s = reverse $ stripDash $ reverse s+#else+ name s = reverse $ stripDash $ stripDash $ reverse s+#endif++idirOpts :: [(c, [String])] -> [(c, [String])]+idirOpts = map (second $ map (drop 2) . filter ("-i"`isPrefixOf`))++spec :: Spec+spec = do+ describe "getComponents" $ do+ it "throws an exception if the cabal file is broken" $ do+ let tdir = "test/data/broken-cabal"+ runD' tdir getComponents `shouldThrow` anyIOException++ it "handles sandboxes correctly" $ do+ let tdir = "test/data/cabal-project"+ cwd <- getCurrentDirectory++ -- TODO: ChSetupHsName should also have sandbox stuff, see related+ -- comment in cabal-helper+ opts <- map gmcGhcOpts . filter ((/= ChSetupHsName) . gmcName) <$> runD' tdir getComponents++ bp <- buildPlatform readProcess+ if ghcVersion < 706+ then forM_ opts (\o -> o `shouldContain` ["-no-user-package-conf","-package-conf", cwd </> "test/data/cabal-project/.cabal-sandbox/"++ghcSandboxPkgDbDir bp])+ else forM_ opts (\o -> o `shouldContain` ["-no-user-package-db","-package-db",cwd </> "test/data/cabal-project/.cabal-sandbox/"++ghcSandboxPkgDbDir bp])++#if !MIN_VERSION_ghc(7,8,0)+ it "handles stack project" $ do+ let tdir = "test/data/stack-project"+ [ghcOpts] <- map gmcGhcOpts . filter ((==ChExeName "new-template-exe") . gmcName) <$> runD' tdir getComponents+ let pkgs = pkgOptions ghcOpts+ sort pkgs `shouldBe` ["base", "bytestring"]+#endif++ it "extracts build dependencies" $ do+ let tdir = "test/data/cabal-project"+ opts <- map gmcGhcOpts <$> runD' tdir getComponents+ let ghcOpts:_ = opts+ pkgs = pkgOptions ghcOpts+ pkgs `shouldBe` ["Cabal","base","template-haskell"]++ it "uses non default flags and preserves them across reconfigures" $ do+ let tdir = "test/data/cabal-flags"+ _ <- withDirectory_ tdir $+ readProcess "cabal" ["configure", "-ftest-flag"] ""++ let test = do+ opts <- map gmcGhcOpts <$> runD' tdir getComponents+ let ghcOpts = head opts+ pkgs = pkgOptions ghcOpts+ pkgs `shouldBe` ["Cabal","base"]++ test++ touch $ tdir </> "cabal-flags.cabal"++ test++touch :: FilePath -> IO ()+touch fn = do+ f <- readFile fn+ writeFile (fn <.> "tmp") f+ renameFile (fn <.> "tmp") fn
+ test/CaseSplitSpec.hs view
@@ -0,0 +1,81 @@+{-# LANGUAGE CPP #-}+module CaseSplitSpec where++import GhcMod+import Test.Hspec+import TestUtils+import Dir++main :: IO ()+main = do+ hspec spec++spec :: Spec+spec = do+ describe "case split" $ do+#if __GLASGOW_HASKELL__ >= 708+ it "does not blow up on HsWithBndrs panic" $ do+ withDirectory_ "test/data/case-split" $ do+ res <- runD $ splits "Vect.hs" 24 10+ res `shouldBe` "24 1 24 30"+++ " \"mlAppend Nil y = _mlAppend_body\NUL"+++ "mlAppend (Cons x1 x2) y = _mlAppend_body\"\n"++ it "works with case expressions" $ do+ withDirectory_ "test/data/case-split" $ do+ res <- runD $ splits "Vect.hs" 28 20+ res `shouldBe` "28 19 28 39"+++ " \"Nil -> _mlAppend_body\NUL"+++ " (Cons x'1 x'2) -> _mlAppend_body\"\n"++ it "works with where clauses" $ do+ withDirectory_ "test/data/case-split" $ do+ res <- runD $ splits "Vect.hs" 34 17+ res `shouldBe` "34 5 34 43"+++ " \"mlReverse' Nil accum = _mlReverse_body\NUL"+++ " mlReverse' (Cons xs'1 xs'2) accum = _mlReverse_body\"\n"++ it "works with let bindings" $ do+ withDirectory_ "test/data/case-split" $ do+ res <- runD $ splits "Vect.hs" 38 33+ res `shouldBe` "38 21 38 59"+++ " \"mlReverse' Nil accum = _mlReverse_body\NUL"+++ " mlReverse' (Cons xs'1 xs'2) accum = _mlReverse_body\"\n"+#else+ it "does not blow up on HsWithBndrs panic" $ do+ withDirectory_ "test/data/case-split" $ do+ res <- runD $ splits "Vect706.hs" 24 10+ res `shouldBe` "24 1 24 25"+++ " \"mlAppend Nil y = undefined\NUL"+++ "mlAppend (Cons x1 x2) y = undefined\"\n"++ it "works with case expressions" $ do+ withDirectory_ "test/data/case-split" $ do+ res <- runD $ splits "Vect706.hs" 28 20+ res `shouldBe` "28 19 28 34"+++ " \"Nil -> undefined\NUL"+++ " (Cons x'1 x'2) -> undefined\"\n"++ it "works with where clauses" $ do+ withDirectory_ "test/data/case-split" $ do+ res <- runD $ splits "Vect706.hs" 34 17+ res `shouldBe` "34 5 34 37"+++ " \"mlReverse' Nil accum = undefined\NUL"+++ " mlReverse' (Cons xs'1 xs'2) accum = undefined\"\n"++ it "works with let bindings" $ do+ withDirectory_ "test/data/case-split" $ do+ res <- runD $ splits "Vect706.hs" 38 33+ res `shouldBe` "38 21 38 53"+++ " \"mlReverse' Nil accum = undefined\NUL"+++ " mlReverse' (Cons xs'1 xs'2) accum = undefined\"\n"++#endif+ it "doesn't crash when source doesn't make sense" $+ withDirectory_ "test/data/case-split" $ do+ res <- runD $ splits "Crash.hs" 4 6+#if __GLASGOW_HASKELL__ < 710+ res `shouldBe` "4 1 4 19 \"test x = undefined\"\n"+#else+ res `shouldBe` ""+#endif
test/CheckSpec.hs view
@@ -1,9 +1,10 @@ {-# LANGUAGE CPP #-} module CheckSpec where -import Data.List (isSuffixOf, isInfixOf, isPrefixOf)-import Language.Haskell.GhcMod-import System.FilePath+import GhcMod++import Data.List+import System.Process import Test.Hspec import TestUtils@@ -14,38 +15,66 @@ describe "checkSyntax" $ do it "works even if an executable depends on the library defined in the same cabal file" $ do withDirectory_ "test/data/ghc-mod-check" $ do- res <- runID $ checkSyntax ["main.hs"]+ res <- runD $ checkSyntax ["main.hs"] res `shouldBe` "main.hs:5:1:Warning: Top-level binding with no type signature: main :: IO ()\n" + it "works even if a module imports another module from a different directory" $ do withDirectory_ "test/data/check-test-subdir" $ do- res <- runID $ checkSyntax ["test/Bar/Baz.hs"]+ _ <- system "cabal configure --enable-tests"+ res <- runD $ checkSyntax ["test/Bar/Baz.hs"] res `shouldSatisfy` (("test" </> "Foo.hs:3:1:Warning: Top-level binding with no type signature: foo :: [Char]\n") `isSuffixOf`) it "detects cyclic imports" $ do- withDirectory_ "test/data" $ do- res <- runID $ checkSyntax ["Mutual1.hs"]+ withDirectory_ "test/data/import-cycle" $ do+ res <- runD $ checkSyntax ["Mutual1.hs"] res `shouldSatisfy` ("Module imports form a cycle" `isInfixOf`) it "works with modules using QuasiQuotes" $ do- withDirectory_ "test/data" $ do- res <- runID $ checkSyntax ["Baz.hs"]- res `shouldSatisfy` ("Baz.hs:5:1:Warning:" `isPrefixOf`)+ withDirectory_ "test/data/quasi-quotes" $ do+ res <- runD $ checkSyntax ["QuasiQuotes.hs"]+ res `shouldSatisfy` ("QuasiQuotes.hs:6:1:Warning:" `isInfixOf`) #if __GLASGOW_HASKELL__ >= 708 it "works with modules using PatternSynonyms" $ do withDirectory_ "test/data/pattern-synonyms" $ do- res <- runID $ checkSyntax ["B.hs"]+ res <- runD $ checkSyntax ["B.hs"] res `shouldSatisfy` ("B.hs:6:9:Warning:" `isPrefixOf`) #endif it "works with foreign exports" $ do- withDirectory_ "test/data" $ do- res <- runID $ checkSyntax ["ForeignExport.hs"]+ withDirectory_ "test/data/foreign-export" $ do+ res <- runD $ checkSyntax ["ForeignExport.hs"] res `shouldBe` "" context "when no errors are found" $ do it "doesn't output an empty line" $ do- withDirectory_ "test/data/ghc-mod-check/Data" $ do- res <- runID $ checkSyntax ["Foo.hs"]+ withDirectory_ "test/data/ghc-mod-check/lib/Data" $ do+ res <- runD $ checkSyntax ["Foo.hs"] res `shouldBe` ""++#if __GLASGOW_HASKELL__ >= 708+-- See https://github.com/kazu-yamamoto/ghc-mod/issues/507+ it "emits warnings generated in GHC's desugar stage" $ do+ withDirectory_ "test/data/check-missing-warnings" $ do+ res <- runD $ checkSyntax ["DesugarWarnings.hs"]+ res `shouldSatisfy` ("DesugarWarnings.hs:4:9:Warning: Pattern match(es) are non-exhaustive\NULIn a case alternative: Patterns not matched:" `isPrefixOf`)+#endif++ it "works with cabal builtin preprocessors" $ do+ withDirectory_ "test/data/cabal-preprocessors" $ do+ _ <- system "cabal clean"+ _ <- system "cabal build"+ res <- runD $ checkSyntax ["Main.hs"]+ res `shouldBe` "Preprocessed.hsc:3:1:Warning: Top-level binding with no type signature: warning :: ()\n"++ it "Uses the right qualification style" $ do+ withDirectory_ "test/data/nice-qualification" $ do+ res <- runD $ checkSyntax ["NiceQualification.hs"]+#if __GLASGOW_HASKELL__ >= 800+ res `shouldBe` "NiceQualification.hs:4:8:\8226 Couldn't match expected type \8216IO ()\8217 with actual type \8216[Char]\8217\NUL\8226 In the expression: \"wrong type\"\NUL In an equation for \8216main\8217: main = \"wrong type\"\n"+#elif __GLASGOW_HASKELL__ >= 708+ res `shouldBe` "NiceQualification.hs:4:8:Couldn't match expected type \8216IO ()\8217 with actual type \8216[Char]\8217\NULIn the expression: \"wrong type\"\NULIn an equation for \8216main\8217: main = \"wrong type\"\n"+#else+ res `shouldBe` "NiceQualification.hs:4:8:Couldn't match expected type `IO ()' with actual type `[Char]'\NULIn the expression: \"wrong type\"\NULIn an equation for `main': main = \"wrong type\"\n"+#endif
+ test/CradleSpec.hs view
@@ -0,0 +1,67 @@+module CradleSpec where++import Control.Applicative+import Data.List (isSuffixOf)+import GhcMod.Cradle+import GhcMod.Types+import System.Directory (canonicalizePath)+import System.FilePath (pathSeparator)+import Test.Hspec+import TestUtils+import Prelude++import Dir++clean_ :: IO Cradle -> IO Cradle+clean_ f = do+ crdl <- f+ cleanupCradle crdl+ return crdl++relativeCradle :: FilePath -> Cradle -> Cradle+relativeCradle dir crdl = crdl {+ cradleCurrentDir = toRelativeDir dir $ cradleCurrentDir crdl+ , cradleRootDir = toRelativeDir dir $ cradleRootDir crdl+ , cradleCabalFile = toRelativeDir dir <$> cradleCabalFile crdl+ }++-- Work around GHC 7.2.2 where `canonicalizePath "/"` returns "/.".+stripLastDot :: FilePath -> FilePath+stripLastDot path+ | (pathSeparator:'.':"") `isSuffixOf` path = init path+ | otherwise = path++spec :: Spec+spec = do+ describe "findCradle" $ do+ it "returns the current directory" $ do+ withDirectory_ "/" $ do+ curDir <- stripLastDot <$> canonicalizePath "/"+ res <- clean_ $ runGmOutDef $ findCradleNoLog $ optPrograms defaultOptions+ cradleCurrentDir res `shouldBe` curDir+ cradleRootDir res `shouldBe` curDir+ cradleCabalFile res `shouldBe` Nothing++ it "finds a cabal file and a sandbox" $ do+ withDirectory "test/data/cabal-project/subdir1/subdir2" $ \dir -> do+ res <- relativeCradle dir <$> clean_ (runGmOutDef $ findCradleNoLog $ optPrograms defaultOptions)++ cradleCurrentDir res `shouldBe`+ "test/data/cabal-project/subdir1/subdir2"++ cradleRootDir res `shouldBe` "test/data/cabal-project"++ cradleCabalFile res `shouldBe`+ Just ("test/data/cabal-project/cabalapi.cabal")++ it "works even if a sandbox config file is broken" $ do+ withDirectory "test/data/broken-sandbox" $ \dir -> do+ res <- relativeCradle dir <$> clean_ (runGmOutDef $ findCradleNoLog $ optPrograms defaultOptions)+ cradleCurrentDir res `shouldBe`+ "test" </> "data" </> "broken-sandbox"++ cradleRootDir res `shouldBe`+ "test" </> "data" </> "broken-sandbox"++ cradleCabalFile res `shouldBe`+ Just ("test" </> "data" </> "broken-sandbox" </> "dummy.cabal")
+ test/CustomPackageDbSpec.hs view
@@ -0,0 +1,35 @@+module CustomPackageDbSpec where++import GhcMod.CabalHelper+import GhcMod.CustomPackageDb+import GhcMod.Error+import System.Process+import Test.Hspec+import Prelude++import Dir+import TestUtils++spec :: Spec+spec = do+ describe "getCustomPkgDbStack" $ do+ it "works" $ do+ let tdir = "test/data/custom-cradle"+ Just stack <- runD' tdir $ getCustomPkgDbStack+ stack `shouldBe` [ GlobalDb+ , UserDb+ , PackageDb "package-db-a"+ , PackageDb "package-db-b"+ , PackageDb "package-db-c"+ ]++ describe "getPackageDbStack'" $ do+ it "fixes out of sync custom pkg-db stack" $ do+ withDirectory_ "test/data/custom-cradle" $ do+ _ <- system "cabal configure"+ (s, s') <- runD $ do+ Just stack <- getCustomPkgDbStack+ withCabal $ do+ stack' <- getCabalPackageDbStack+ return (stack, stack')+ s' `shouldBe` s
test/Dir.hs view
@@ -1,9 +1,15 @@-module Dir where+module Dir (+ module Dir+ , getCurrentDirectory+ , (</>)+ ) where import Control.Exception as E import Data.List (isPrefixOf) import System.Directory-import System.FilePath (addTrailingPathSeparator)+import System.FilePath (addTrailingPathSeparator,(</>))++ withDirectory_ :: FilePath -> IO a -> IO a withDirectory_ dir action = bracket getCurrentDirectory
+ test/FileMappingSpec.hs view
@@ -0,0 +1,259 @@+module FileMappingSpec where++import GhcMod.FileMapping+import GhcMod.Utils (withMappedFile)+import Test.Hspec+import TestUtils+import qualified Data.Map as M+import Dir+import System.IO.Temp+import System.Directory++import GhcMod++spec :: Spec+spec = do+ describe "loadMappedFile" $ do+ it "inserts a given FilePath FileMapping into state with canonicalized path" $ do+ withDirectory_ "test/data/file-mapping" $ do+ mappedFiles <- runD $ do+ loadMappedFile "File.hs" "File.hs"+ getMMappedFiles+ dir <- getCurrentDirectory+ show mappedFiles `shouldBe` show (M.fromList [(dir </> "File.hs", FileMapping "File.hs" False)])+ it "should try to guess a canonical name if file doesn't exist" $ do+ withDirectory_ "test/data/file-mapping" $ do+ mappedFiles <- runD $ do+ loadMappedFile "NonExistantFile.hs" "File.hs"+ getMMappedFiles+ dir <- getCurrentDirectory+ show mappedFiles `shouldBe` show (M.fromList [(dir </> "NonExistantFile.hs", FileMapping "File.hs" False)])++ describe "loadMappedFileSource" $ do+ it "inserts a given FilePath FileMapping into state with canonicalized path" $ do+ withDirectory_ "test/data/file-mapping" $ do+ mappedFiles <- runD $ do+ loadMappedFileSource "File.hs" "main :: IO ()"+ getMMappedFiles+ dir <- getCurrentDirectory+ -- TODO+ M.toList mappedFiles `shouldSatisfy` \[(fn, FileMapping _to True)] ->+ fn == dir </> "File.hs"+ it "should try to guess a canonical name if file doesn't exist" $ do+ withDirectory_ "test/data/file-mapping" $ do+ mappedFiles <- runD $ do+ loadMappedFileSource "NonExistantFile.hs" "main :: IO ()"+ getMMappedFiles+ dir <- getCurrentDirectory+ -- TODO+ M.toList mappedFiles `shouldSatisfy` \[(fn, FileMapping _to True)] ->+ fn == dir </> "NonExistantFile.hs"++ describe "unloadMappedFile" $ do+ it "removes a given FilePath from state" $ do+ withDirectory_ "test/data/file-mapping" $ do+ mappedFiles <- runD $ do+ loadMappedFile "File.hs" "File2.hs"+ unloadMappedFile "File.hs"+ getMMappedFiles+ show mappedFiles `shouldBe` show (M.fromList ([] :: [(FilePath, FileMapping)]))+ it "should work even if file does not exist" $ do+ withDirectory_ "test/data/file-mapping" $ do+ mappedFiles <- runD $ do+ loadMappedFile "NonExistantFile.hs" "File2.hs"+ unloadMappedFile "NonExistantFile.hs"+ getMMappedFiles+ show mappedFiles `shouldBe` show (M.fromList ([] :: [(FilePath, FileMapping)]))+ it "should remove created temporary files" $ do+ withDirectory_ "test/data/file-mapping" $ do+ dir <- getCurrentDirectory+ fileExists <- runD $ do+ loadMappedFileSource "NonExistantFile.hs" "main :: IO ()"+ fp <- maybe undefined fmPath `fmap` lookupMMappedFile (dir </> "NonExistantFile.hs")+ unloadMappedFile "NonExistantFile.hs"+ liftIO $ doesFileExist fp+ not fileExists `shouldBe` True++ describe "withMappedFile" $ do+ it "checks if there is a redirected file and calls and action with its FilePath" $ do+ withDirectory_ "test/data/file-mapping" $ do+ res <- runD $ do+ loadMappedFile "File.hs" "File_Redir.hs"+ withMappedFile "File.hs" return+ res `shouldBe` "File_Redir.hs"+ it "checks if there is an in-memory file and calls and action with temporary file" $ do+ withDirectory_ "test/data/file-mapping" $ do+ (fn, src) <- runD $ do+ loadMappedFileSource "File.hs" "main = test"+ withMappedFile "File.hs" $ \fn -> do+ src <- liftIO $ readFile fn+ return (fn, src)+ fn `shouldSatisfy` (/="File.hs")+ src `shouldBe` "main = test"+ it "runs action with original filename if there is no mapping" $ do+ withDirectory_ "test/data/file-mapping" $ do+ fn <- runD $ do+ withMappedFile "File.hs" return+ fn `shouldBe` "File.hs"++ describe "integration tests" $ do+ it "checks redirected file if one is specified and outputs original filename" $ do+ withDirectory_ "test/data/file-mapping" $ do+ let fm = [("File.hs", "File_Redir.hs")]+ res <- run defaultOptions $ do+ mapM_ (uncurry loadMappedFile) fm+ checkSyntax ["File.hs"]+ res `shouldBe` "File.hs:1:1:Warning: Top-level binding with no type signature: main :: IO ()\n"+ it "checks in-memory file if one is specified and outputs original filename" $ do+ withDirectory_ "test/data/file-mapping" $ do+ let fm = [("File.hs", "main = putStrLn \"Hello World!\"\n")]+ res <- run defaultOptions $ do+ mapM_ (uncurry loadMappedFileSource) fm+ checkSyntax ["File.hs"]+ res `shouldBe` "File.hs:1:1:Warning: Top-level binding with no type signature: main :: IO ()\n"+ it "should work even if file doesn't exist" $ do+ withDirectory_ "test/data/file-mapping" $ do+ let fm = [("Nonexistent.hs", "main = putStrLn \"Hello World!\"\n")]+ res <- run defaultOptions $ do+ mapM_ (uncurry loadMappedFileSource) fm+ checkSyntax ["Nonexistent.hs"]+ res `shouldBe` "Nonexistent.hs:1:1:Warning: Top-level binding with no type signature: main :: IO ()\n"+ it "lints redirected file if one is specified and outputs original filename" $ do+ withDirectory_ "test/data/file-mapping" $ do+ res <- runD $ do+ loadMappedFile "File.hs" "File_Redir_Lint.hs"+ lint lintOpts "File.hs"+ res `shouldBe` "File.hs:4:1: Warning: Eta reduce\NULFound:\NUL func a b = (*) a b\NULWhy not:\NUL func = (*)\n"+ it "lints in-memory file if one is specified and outputs original filename" $ do+ withDirectory_ "test/data/file-mapping" $ do+ res <- runD $ do+ loadMappedFileSource "File.hs" "func a b = (++) a b\n"+ lint lintOpts "File.hs"+ res `shouldBe` "File.hs:1:1: Warning: Eta reduce\NULFound:\NUL func a b = (++) a b\NULWhy not:\NUL func = (++)\n"+ it "shows types of the expression for redirected files" $ do+ let tdir = "test/data/file-mapping"+ res <- runD' tdir $ do+ loadMappedFile "File.hs" "File_Redir_Lint.hs"+ types False "File.hs" 4 12+ res `shouldBe` "4 12 4 15 \"a -> a -> a\"\n4 12 4 17 \"a -> a\"\n4 12 4 19 \"a\"\n4 1 4 19 \"a -> a -> a\"\n"+ it "shows types of the expression with constraints for redirected files" $ do --+ let tdir = "test/data/file-mapping"+ res <- runD' tdir $ do+ loadMappedFile "File.hs" "File_Redir_Lint.hs"+ types True "File.hs" 4 12+ res `shouldBe` "4 12 4 15 \"a -> a -> a\"\n4 12 4 17 \"a -> a\"\n4 12 4 19 \"a\"\n4 1 4 19 \"Num a => a -> a -> a\"\n"+ it "shows types of the expression for in-memory files" $ do+ let tdir = "test/data/file-mapping"+ res <- runD' tdir $ do+ loadMappedFileSource "File.hs" "main = putStrLn \"Hello!\""+ types False "File.hs" 1 14+ res `shouldBe` "1 8 1 16 \"String -> IO ()\"\n1 8 1 25 \"IO ()\"\n1 1 1 25 \"IO ()\"\n"+ it "shows info for the expression for redirected files" $ do+ let tdir = "test/data/file-mapping"+ res <- runD' tdir $ do+ loadMappedFile "File.hs" "File_Redir_Lint.hs"+ info "File.hs" $ Expression "func"+ res `shouldBe` "func :: Num a => a -> a -> a \t-- Defined at File.hs:4:1\n"+ it "shows info for the expression for in-memory files" $ do+ let tdir = "test/data/file-mapping"+ res <- runD' tdir $ do+ loadMappedFileSource "File.hs" "module File where\n\ntestfun = putStrLn \"Hello!\""+ info "File.hs" $ Expression "testfun"+ res `shouldBe` "testfun :: IO () \t-- Defined at File.hs:3:1\n"++ describe "preprocessor tests" $ do+ it "checks redirected file if one is specified and outputs original filename" $ do+ withDirectory_ "test/data/file-mapping/preprocessor" $ do+ let fm = [("File.hs", "File_Redir.hs")]+ res <- run defaultOptions $ do+ mapM_ (uncurry loadMappedFile) fm+ checkSyntax ["File.hs"]+ res `shouldBe` "File.hs:3:1:Warning: Top-level binding with no type signature: main :: IO ()\n"+ it "works with full path as well" $ do+ withDirectory_ "test/data/file-mapping/preprocessor" $ do+ cwd <- getCurrentDirectory+ let fm = [("File.hs", cwd </> "File_Redir.hs")]+ res <- run defaultOptions $ do+ mapM_ (uncurry loadMappedFile) fm+ checkSyntax ["File.hs"]+ res `shouldBe` "File.hs:3:1:Warning: Top-level binding with no type signature: main :: IO ()\n"+ it "checks in-memory file" $ do+ withDirectory_ "test/data/file-mapping/preprocessor" $ do+ src <- readFile "File_Redir.hs"+ let fm = [("File.hs", src)]+ res <- run defaultOptions $ do+ mapM_ (uncurry loadMappedFileSource) fm+ checkSyntax ["File.hs"]+ res `shouldBe` "File.hs:3:1:Warning: Top-level binding with no type signature: main :: IO ()\n"+ it "lints redirected file if one is specified and outputs original filename" $ do+ withDirectory_ "test/data/file-mapping/preprocessor" $ do+ res <- runD $ do+ loadMappedFile "File.hs" "File_Redir_Lint.hs"+ lint lintOpts "File.hs"+ res `shouldBe` "File.hs:6:1: Warning: Eta reduce\NULFound:\NUL func a b = (*) a b\NULWhy not:\NUL func = (*)\n"+ it "lints in-memory file if one is specified and outputs original filename" $ do+ withDirectory_ "test/data/file-mapping/preprocessor" $ do+ src <- readFile "File_Redir_Lint.hs"+ res <- runD $ do+ loadMappedFileSource "File.hs" src+ lint lintOpts "File.hs"+ res `shouldBe` "File.hs:6:1: Warning: Eta reduce\NULFound:\NUL func a b = (*) a b\NULWhy not:\NUL func = (*)\n"+ describe "literate haskell tests" $ do+ it "checks redirected file if one is specified and outputs original filename" $ do+ withDirectory_ "test/data/file-mapping/lhs" $ do+ let fm = [("File.lhs", "File_Redir.lhs")]+ res <- run defaultOptions $ do+ mapM_ (uncurry loadMappedFile) fm+ checkSyntax ["File.lhs"]+ res `shouldBe` "File.lhs:1:3:Warning: Top-level binding with no type signature: main :: IO ()\n"+ it "checks in-memory file if one is specified and outputs original filename" $ do+ withDirectory_ "test/data/file-mapping/lhs" $ do+ src <- readFile "File_Redir.lhs"+ let fm = [("File.lhs", src)]+ res <- run defaultOptions $ do+ mapM_ (uncurry loadMappedFileSource) fm+ checkSyntax ["File.lhs"]+ res `shouldBe` "File.lhs:1:3:Warning: Top-level binding with no type signature: main :: IO ()\n"+ -- NOTE: There is a bug in hlint that prevents it from linting lhs files.+ -- it "lints redirected file if one is specified and outputs original filename" $ do+ -- withDirectory_ "test/data/file-mapping/lhs" $ do+ -- res <- runD $ do+ -- loadMappedFile "File.lhs" (RedirectedMapping "File_Redir_Lint.lhs")+ -- lint "File.lhs"+ -- res `shouldBe` "File.lhs:6:1: Error: Eta reduce\NULFound:\NUL func a b = (*) a b\NULWhy not:\NUL func = (*)\n"+ -- it "lints in-memory file if one is specified and outputs original filename" $ do+ -- withDirectory_ "test/data/file-mapping/lhs" $ do+ -- src <- readFile "File_Redir_Lint.lhs"+ -- res <- runD $ do+ -- loadMappedFile "File.lhs" (MemoryMapping $ Just src)+ -- lint "File.lhs"+ -- res `shouldBe` "File.lhs:6:1: Error: Eta reduce\NULFound:\NUL func a b = (*) a b\NULWhy not:\NUL func = (*)\n"+ describe "template haskell" $ do+ it "works with a redirected module using TemplateHaskell" $ do+ withSystemTempDirectory "ghc-mod-test" $ \tmpdir -> do+ srcFoo <- readFile "test/data/template-haskell/Foo.hs"+ srcBar <- readFile "test/data/template-haskell/Bar.hs"+ withDirectory_ "test/data/file-mapping" $ do+ writeFile (tmpdir </> "Foo_Redir.hs") srcFoo+ writeFile (tmpdir </> "Bar_Redir.hs") srcBar+ let fm = [("Foo.hs", tmpdir </> "Foo_Redir.hs")+ ,("Bar.hs", tmpdir </> "Bar_Redir.hs")]+ res <- run defaultOptions $ do+ mapM_ (uncurry loadMappedFile) fm+ types False "Bar.hs" 5 1+ res `shouldBe` unlines ["5 1 5 20 \"[Char]\""]+ it "works with a memory module using TemplateHaskell" $ do+ srcFoo <- readFile "test/data/template-haskell/Foo.hs"+ srcBar <- readFile "test/data/template-haskell/Bar.hs"+ withDirectory_ "test/data/file-mapping" $ do+ let fm = [("Foo.hs", srcFoo)+ ,("Bar.hs", srcBar)]+ res <- run defaultOptions $ do+ mapM_ (uncurry loadMappedFileSource) fm+ types False "Bar.hs" 5 1+ res `shouldBe` unlines ["5 1 5 20 \"[Char]\""]+++lintOpts :: LintOpts+lintOpts =+ defaultLintOpts { optLintHlintOpts = ["--ignore=Use module export list"] }
+ test/FindSpec.hs view
@@ -0,0 +1,13 @@+{-# LANGUAGE OverloadedStrings #-}+module FindSpec where++import GhcMod.Exe.Find+import Test.Hspec+import TestUtils++spec :: Spec+spec = do+ describe "db <- loadSymbolDb" $ do+ it "lookupSymbol' db \"head\" contains at least `Data.List'" $ do+ db <- runD $ loadSymbolDb+ lookupSym "head" db `shouldContain` [ModuleString "Data.List"]
test/FlagSpec.hs view
@@ -1,13 +1,14 @@ module FlagSpec where import Control.Applicative-import Language.Haskell.GhcMod+import GhcMod import Test.Hspec import TestUtils+import Prelude spec :: Spec spec = do describe "flags" $ do- it "contains at least `-fno-warn-orphans'" $ do+ it "contains at least `-fprint-explicit-foralls" $ do f <- runD $ lines <$> flags- f `shouldContain` ["-fno-warn-orphans"]+ f `shouldContain` ["-fprint-explicit-foralls"]
test/GhcPkgSpec.hs view
@@ -1,29 +1,23 @@-{-# LANGUAGE CPP #-} module GhcPkgSpec where -import Language.Haskell.GhcMod.GhcPkg-#if __GLASGOW_HASKELL__ <= 706-import Language.Haskell.GhcMod.Types-#endif--import System.Directory-import System.FilePath ((</>))+import GhcMod.GhcPkg+import GhcMod.CabalHelper+import GhcMod.CustomPackageDb import Test.Hspec+import System.Process (system) +import Dir+import TestUtils+ spec :: Spec spec = do- describe "getSandboxDb" $ do--- ghc < 7.8-#if __GLASGOW_HASKELL__ <= 706- it "does include a sandbox with ghc < 7.8" $ do- cwd <- getCurrentDirectory- getPackageDbStack "test/data/" `shouldReturn` [GlobalDb, PackageDb $ cwd </> "test/data/.cabal-sandbox/i386-osx-ghc-7.6.3-packages.conf.d"]-#endif-- it "can parse a config file and extract the sandbox package-db" $ do- cwd <- getCurrentDirectory- pkgDb <- getSandboxDb "test/data/"- pkgDb `shouldBe` (cwd </> "test/data/.cabal-sandbox/i386-osx-ghc-7.6.3-packages.conf.d")-- it "throws an error if the sandbox config file is broken" $ do- getSandboxDb "test/data/broken-sandbox" `shouldThrow` anyException+ describe "getPackageDbStack'" $ do+ it "fixes out of sync custom pkg-db stack" $ do+ withDirectory_ "test/data/custom-cradle" $ do+ _ <- system "cabal configure"+ (s, s') <- runD $ do+ Just stack <- getCustomPkgDbStack+ withCabal $ do+ stack' <- getPackageDbStack+ return (stack, stack')+ s' `shouldBe` s
+ test/HomeModuleGraphSpec.hs view
@@ -0,0 +1,178 @@+-- ghc-mod: Happy Haskell Hacking+-- Copyright (C) 2015 Daniel Gröber <dxld ÄT darkboxed DOT org>+--+-- This program is free software: you can redistribute it and/or modify+-- it under the terms of the GNU Affero General Public License as published by+-- the Free Software Foundation, either version 3 of the License, or+-- (at your option) any later version.+--+-- This program is distributed in the hope that it will be useful,+-- but WITHOUT ANY WARRANTY; without even the implied warranty of+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+-- GNU Affero General Public License for more details.+--+-- You should have received a copy of the GNU Affero General Public License+-- along with this program. If not, see <http://www.gnu.org/licenses/>.++{-# LANGUAGE OverloadedStrings #-}++module HomeModuleGraphSpec where++import GhcMod.HomeModuleGraph+import GhcMod.LightGhc+import TestUtils++import GHC+import qualified Data.Map as Map+import qualified Data.Set as Set+import Data.Maybe++import Test.Hspec++runAGhc :: [GHCOption] -> (HscEnv -> LightGhc a) -> IO a+runAGhc opts action = withLightHscEnv opts $ \env -> do+ runLightGhc env $ getSession >>= action++hmGraph :: FilePath -> [String] -> String -> IO GmModuleGraph+hmGraph dir opts mn = runAGhc opts $ \env -> liftIO $ do+ runD' dir $ do+ smp <- liftIO $ findModulePathSet env [mkModuleName mn]+ homeModuleGraph env smp++uhmGraph :: FilePath -> [String] -> String -> String -> GmModuleGraph -> IO GmModuleGraph+uhmGraph dir opts mn umn g = runAGhc opts $ \env -> liftIO $ do+ runD' dir $ do+ smp <- liftIO $ findModulePathSet env [mkModuleName mn]+ usmp <- liftIO $ findModulePathSet env [mkModuleName umn]+ updateHomeModuleGraph env g smp usmp++mapMap :: (Ord k, Ord k')+ => (k -> k') -> (a -> a') -> Map.Map k a -> Map.Map k' a'+mapMap fk fa = Map.mapKeys fk . Map.map fa++mapMpFn :: (FilePath -> FilePath) -> ModulePath -> ModulePath+mapMpFn f (ModulePath mn fn) = ModulePath mn (f fn)++mp :: ModuleName -> ModulePath+mp mn = ModulePath mn $ moduleNameString mn ++ ".hs"++spec :: Spec+spec = do+ describe "reachable" $ do+ let+ smp =+ Set.fromList+ [ mp "A"+ , mp "B"+ , mp "C"+ , mp "D"+ , mp "E"+ , mp "F"+ , mp "G"+ , mp "H"+ , mp "I"+ ]++ moduleMap = mkModuleMap smp++ completeGraph =+ Map.map (Set.map lookupMM) . Map.mapKeys lookupMM++ lookupMM = fromJust . flip Map.lookup moduleMap++ graph = completeGraph $+ Map.fromList+ [ ("A", Set.fromList ["B"])+ , ("B", Set.fromList ["C", "D"])+ , ("C", Set.fromList ["F"])+ , ("D", Set.fromList ["E"])+ , ("E", Set.fromList [])+ , ("F", Set.fromList [])+ , ("G", Set.fromList [])+ , ("H", Set.fromList [])+ , ("I", Set.fromList [])+ ]++ really_reachable =+ Set.fromList+ [ mp "A"+ , mp "B"+ , mp "C"+ , mp "D"+ , mp "E"+ , mp "F"+ ]++ g = GmModuleGraph {+ gmgGraph = graph+ }++ it "reachable Set.empty g == Set.empty" $ do+ reachable Set.empty g `shouldBe` Set.empty++ it "lists only reachable nodes" $ do+ reachable (Set.fromList [mp "A"]) g `shouldBe` really_reachable+++ describe "homeModuleGraph" $ do+ it "cycles don't break it" $ do+ let tdir = "test/data/home-module-graph/cycle"+ g <- hmGraph tdir [] "A"+ gmgGraph g `shouldBe`+ Map.fromList+ [ (mp "A", Set.fromList [mp "B"])+ , (mp "B", Set.fromList [mp "A"])+ ]++ it "follows imports" $ do+ let tdir = "test/data/home-module-graph/indirect"+ g <- hmGraph tdir [] "A"+ gmgGraph g `shouldBe`+ Map.fromList+ [ (mp "A", Set.fromList [mp "A1", mp "A2", mp "A3"])+ , (mp "A1", Set.fromList [mp "B"])+ , (mp "A2", Set.fromList [mp "C"])+ , (mp "A3", Set.fromList [mp "B"])+ , (mp "B", Set.fromList [])+ , (mp "C", Set.fromList [])+ ]++ it "returns partial results on parse errors" $ do+ let tdir = "test/data/home-module-graph/errors"+ g <- hmGraph tdir [] "A"+ gmgGraph g `shouldBe`+ Map.fromList+ [ (mp "A", Set.fromList [mp "A1", mp "A2", mp "A3"])+ , (mp "A1", Set.fromList []) -- parse error here+ , (mp "A2", Set.fromList [])+ , (mp "A3", Set.fromList [mp "B"])+ , (mp "B", Set.fromList [])+ ]++ it "returns partial results on CPP errors" $ do+ let tdir = "test/data/home-module-graph/cpp"+ g <- hmGraph tdir [] "A"+ gmgGraph g `shouldBe`+ Map.fromList+ [ (mp "A", Set.fromList [mp "A1", mp "A2", mp "A3"])+ , (mp "A1", Set.fromList []) -- CPP error here+ , (mp "A2", Set.fromList [])+ , (mp "A3", Set.fromList [mp "B"])+ , (mp "B", Set.fromList [])+ ]++ describe "updateHomeModuleGraph" $ do+ it "removes unreachable nodes" $ do+ let tdir = "test/data/home-module-graph/indirect"+ let tdir' = "test/data/home-module-graph/indirect-update"+ ig <- hmGraph tdir [] "A"+ g <- uhmGraph tdir' [] "A" "A2" ig+ gmgGraph g `shouldBe`+ Map.fromList+ [ (mp "A", Set.fromList [mp "A1", mp "A2", mp "A3"])+ , (mp "A1", Set.fromList [mp "B"])+ , (mp "A2", Set.fromList [])+ , (mp "A3", Set.fromList [mp "B"])+ , (mp "B", Set.fromList [])+ -- C was removed+ ]
test/InfoSpec.hs view
@@ -1,59 +1,66 @@ {-# LANGUAGE CPP #-} module InfoSpec where -import Control.Applicative ((<$>))+import Control.Applicative import Data.List (isPrefixOf)-import Language.Haskell.GhcMod+import GhcMod #if __GLASGOW_HASKELL__ < 706 import System.Environment.Executable (getExecutablePath) #else import System.Environment (getExecutablePath) #endif-import System.Exit import System.FilePath-import System.Process import Test.Hspec import TestUtils-import Dir+import Prelude spec :: Spec spec = do describe "types" $ do it "shows types of the expression and its outers" $ do- withDirectory_ "test/data/ghc-mod-check" $ do- res <- runD $ types "Data/Foo.hs" 9 5- res `shouldBe` "9 5 11 40 \"Int -> a -> a -> a\"\n7 1 11 40 \"Int -> Integer\"\n"+ let tdir = "test/data/ghc-mod-check"+ res <- runD' tdir $ types False "lib/Data/Foo.hs" 9 5+#if __GLASGOW_HASKELL__ >= 800+ res `shouldBe` "9 5 11 40 \"Int -> t -> t -> t\"\n7 1 11 40 \"Int -> Integer\"\n"+#else+ res `shouldBe` "9 5 11 40 \"Int -> a -> a -> a\"\n7 1 11 40 \"Int -> Integer\"\n"+#endif ++ it "shows types of the expression with constraints and its outers" $ do+ let tdir = "test/data/ghc-mod-check"+ res <- runD' tdir $ types True "lib/Data/Foo.hs" 9 5+#if __GLASGOW_HASKELL__ >= 800+ res `shouldBe` "9 5 11 40 \"Num t => Int -> t -> t -> t\"\n7 1 11 40 \"Int -> Integer\"\n"+#else+ res `shouldBe` "9 5 11 40 \"Num a => Int -> a -> a -> a\"\n7 1 11 40 \"Int -> Integer\"\n"+#endif+ it "works with a module using TemplateHaskell" $ do- withDirectory_ "test/data" $ do- res <- runD $ types "Bar.hs" 5 1- res `shouldBe` unlines ["5 1 5 20 \"[Char]\""]+ let tdir = "test/data/template-haskell"+ res <- runD' tdir $ types False "Bar.hs" 5 1+ res `shouldBe` unlines ["5 1 5 20 \"[Char]\""] it "works with a module that imports another module using TemplateHaskell" $ do- withDirectory_ "test/data" $ do- res <- runD $ types "Main.hs" 3 8- res `shouldBe` unlines ["3 8 3 16 \"String -> IO ()\"", "3 8 3 20 \"IO ()\"", "3 1 3 20 \"IO ()\""]+ let tdir = "test/data/template-haskell"+ res <- runD' tdir $ types False "ImportsTH.hs" 3 8+ res `shouldBe` unlines ["3 8 3 16 \"String -> IO ()\"", "3 8 3 20 \"IO ()\"", "3 1 3 20 \"IO ()\""] describe "info" $ do- it "works for non-export functions" $ do- withDirectory_ "test/data" $ do- res <- runD $ info "Info.hs" "fib"- res `shouldSatisfy` ("fib :: Int -> Int" `isPrefixOf`)+ it "works for non exported functions" $ do+ let tdir = "test/data/non-exported"+ res <- runD' tdir $ info "Fib.hs" $ Expression "fib"+ res `shouldSatisfy` ("fib :: Int -> Int" `isPrefixOf`) it "works with a module using TemplateHaskell" $ do- withDirectory_ "test/data" $ do- res <- runD $ info "Bar.hs" "foo"- res `shouldSatisfy` ("foo :: ExpQ" `isPrefixOf`)+ let tdir = "test/data/template-haskell"+ res <- runD' tdir $ info "Bar.hs" $ Expression "foo"+ res `shouldSatisfy` ("foo :: ExpQ" `isPrefixOf`) it "works with a module that imports another module using TemplateHaskell" $ do- withDirectory_ "test/data" $ do- res <- runD $ info "Main.hs" "bar"- res `shouldSatisfy` ("bar :: [Char]" `isPrefixOf`)-- it "doesn't fail on unicode output" $ do- dir <- getDistDir- code <- rawSystem (dir </> "build/ghc-mod/ghc-mod") ["info", "test/data/Unicode.hs", "Unicode", "unicode"]- code `shouldSatisfy` (== ExitSuccess)+ let tdir = "test/data/template-haskell"+ res <- runD' tdir $ info "ImportsTH.hs" $ Expression "bar"+ res `shouldSatisfy` ("bar :: [Char]" `isPrefixOf`) getDistDir :: IO FilePath getDistDir = takeDirectory . takeDirectory . takeDirectory <$> getExecutablePath
test/LangSpec.hs view
@@ -1,9 +1,10 @@ module LangSpec where import Control.Applicative-import Language.Haskell.GhcMod+import GhcMod import Test.Hspec import TestUtils+import Prelude spec :: Spec spec = do
test/LintSpec.hs view
@@ -1,6 +1,6 @@ module LintSpec where -import Language.Haskell.GhcMod+import GhcMod import Test.Hspec import TestUtils @@ -8,10 +8,14 @@ spec = do describe "lint" $ do it "can detect a redundant import" $ do- res <- runD $ lint "test/data/hlint.hs"- res `shouldBe` "test/data/hlint.hs:4:8: Error: Redundant do\NULFound:\NUL do putStrLn \"Hello, world!\"\NULWhy not:\NUL putStrLn \"Hello, world!\"\n"+ res <- runD $ lint lintOpts "test/data/hlint/hlint.hs"+ res `shouldBe` "test/data/hlint/hlint.hs:4:8: Warning: Redundant do\NULFound:\NUL do putStrLn \"Hello, world!\"\NULWhy not:\NUL putStrLn \"Hello, world!\"\n" context "when no suggestions are given" $ do it "doesn't output an empty line" $ do- res <- runD $ lint "test/data/ghc-mod-check/Data/Foo.hs"+ res <- runD $ lint lintOpts "test/data/ghc-mod-check/lib/Data/Foo.hs" res `shouldBe` ""++lintOpts :: LintOpts+lintOpts =+ defaultLintOpts { optLintHlintOpts = ["--ignore=Use module export list"] }
test/ListSpec.hs view
@@ -1,13 +1,14 @@ module ListSpec where import Control.Applicative-import Language.Haskell.GhcMod+import GhcMod import Test.Hspec import TestUtils+import Prelude spec :: Spec spec = do describe "modules" $ do it "contains at least `Data.Map'" $ do- mdls <- runD $ lines <$> modules+ mdls <- runD $ lines <$> modules False mdls `shouldContain` ["Data.Map"]
test/Main.hs view
@@ -1,33 +1,94 @@ {-# LANGUAGE CPP, ScopedTypeVariables #-}-import Spec import Dir import Control.Exception as E import Control.Monad (void)-import Language.Haskell.GhcMod (debugInfo)+import GhcMod (debugInfo) import System.Process+import System.Environment import Test.Hspec import TestUtils +import qualified BrowseSpec+import qualified CabalHelperSpec+import qualified CaseSplitSpec+import qualified CheckSpec+import qualified CradleSpec+import qualified CustomPackageDbSpec+import qualified FileMappingSpec+import qualified FindSpec+import qualified FlagSpec+import qualified GhcPkgSpec+import qualified HomeModuleGraphSpec+import qualified InfoSpec+import qualified LangSpec+import qualified LintSpec+import qualified ListSpec+import qualified MonadSpec+import qualified PathsAndFilesSpec+import qualified ShellParseSpec+import qualified TargetSpec++spec :: Spec+spec = do+ describe "Browse" BrowseSpec.spec+ describe "CabalHelper" CabalHelperSpec.spec+ describe "CaseSplit" CaseSplitSpec.spec+ describe "Check" CheckSpec.spec+ describe "Cradle" CradleSpec.spec+ describe "CustomPackageDb" CustomPackageDbSpec.spec+ describe "FileMapping" FileMappingSpec.spec+ describe "Find" FindSpec.spec+ describe "Flag" FlagSpec.spec+ describe "GhcPkg" GhcPkgSpec.spec+ describe "HomeModuleGraph" HomeModuleGraphSpec.spec+ describe "Info" InfoSpec.spec+ describe "Lang" LangSpec.spec+ describe "Lint" LintSpec.spec+ describe "List" ListSpec.spec+ describe "Monad" MonadSpec.spec+ describe "PathsAndFiles" PathsAndFilesSpec.spec+ describe "ShellParse" ShellParseSpec.spec+ describe "Target" TargetSpec.spec+ main :: IO () main = do- let sandboxes = [ "test/data", "test/data/check-packageid"+#if __GLASGOW_HASKELL__ >= 708+ unsetEnv "GHC_PACKAGE_PATH"+#endif+ let sandboxes = [ "test/data/cabal-project"+ , "test/data/check-packageid" , "test/data/duplicate-pkgver/" , "test/data/broken-cabal/" ] genSandboxCfg dir = withDirectory dir $ \cwdir -> do- system ("sed 's|@CWD@|" ++ cwdir ++ "|g' cabal.sandbox.config.in > cabal.sandbox.config")+ system ("rm cabal.sandbox.config; cabal sandbox init") pkgDirs =- [ "test/data/.cabal-sandbox/i386-osx-ghc-7.6.3-packages.conf.d"+ [ "test/data/cabal-project/.cabal-sandbox/i386-osx-ghc-7.6.3-packages.conf.d" , "test/data/check-packageid/.cabal-sandbox/i386-osx-ghc-7.6.3-packages.conf.d" , "test/data/duplicate-pkgver/.cabal-sandbox/i386-osx-ghc-7.6.3-packages.conf.d"] genGhcPkgCache dir = system $ "ghc-pkg recache --force -f" ++ dir- genSandboxCfg `mapM_` sandboxes- genGhcPkgCache `mapM_` pkgDirs- void $ system "find test -name setup-config -name ghc-mod.cache -exec rm {} \\;"++ let cleanCmd = "git clean -dXf test/data/"+ putStrLn $ "$ " ++ cleanCmd+ void $ system cleanCmd void $ system "cabal --version"- putStrLn $ "ghc-mod was built with Cabal version " ++ VERSION_Cabal void $ system "ghc --version"++ genSandboxCfg `mapM_` sandboxes+ genGhcPkgCache `mapM_` pkgDirs++ let stackDir = "test/data/stack-project"+ void $ withDirectory_ stackDir $ do+ let ghcver = let gvn = show (__GLASGOW_HASKELL__ :: Int)+ (major, minor') = splitAt (length gvn - 2) gvn+ minor = case dropWhile (=='0') minor' of+ "" -> "0"+ x -> x+ in major ++ "." ++ minor+ void $ system $ "sed '$ a resolver: ghc-" ++ ghcver ++ "' stack.yaml.in > stack.yaml"+ void $ system "stack setup"+ void $ system "stack build" (putStrLn =<< runD debugInfo) `E.catch` (\(_ :: E.SomeException) -> return () )
test/MonadSpec.hs view
@@ -1,19 +1,17 @@-{-# LANGUAGE ScopedTypeVariables #-} module MonadSpec where import Test.Hspec-import Dir import TestUtils-import Control.Applicative-import Control.Exception import Control.Monad.Error.Class+import Control.Concurrent+import Control.Exception spec :: Spec spec = do describe "When using GhcModT in a do block" $ it "a pattern match failure causes a call to `fail` on ErrorT in the monad stack" $ do- (a, _)- <- runGhcModT defaultOptions $+ (a, _h)+ <- runGmOutDef $ runGhcModT defaultOptions $ do Just _ <- return Nothing return "hello"@@ -21,19 +19,30 @@ a `shouldBe` (Left $ GMEString "oh noes") describe "runGhcModT" $- it "complains if the cabal file fails to parse while a sandbox is present" $ withDirectory_ "test/data/broken-cabal" $ do- shouldReturnError $ runD' (gmCradle <$> ask)+ it "throws an exception when run in multiple threads" $ do - describe "gmsGet/Put" $- it "work" $ do- (runD $ gmsPut (GhcModState Intelligent) >> gmsGet)- `shouldReturn` (GhcModState Intelligent)+ mv_ex :: MVar (Either SomeException ())+ <- newEmptyMVar+ mv_startup_barrier :: MVar ()+ <- newEmptyMVar - describe "liftIO" $ do- it "converts user errors to GhcModError" $ do- shouldReturnError $- runD' $ liftIO $ throw (userError "hello") >> return ""+ _t1 <- forkOS $ do+ -- wait (inside GhcModT) for t2 to receive the exception+ _ <- runD $ liftIO $ do+ putMVar mv_startup_barrier ()+ readMVar mv_ex+ return () - it "converts a file not found exception to GhcModError" $ do- shouldReturnError $- runD' $ liftIO $ readFile "/DOES_NOT_EXIST" >> return ""+ _t2 <- forkOS $ do+ readMVar mv_startup_barrier -- wait for t1 to be in GhcModT+ res <- try $ runD $ return ()+ res' <- evaluate res+ putMVar mv_ex res'++ ex <- takeMVar mv_ex++ isLeft ex `shouldBe` True++isLeft :: Either a b -> Bool+isLeft (Right _) = False+isLeft (Left _) = True
+ test/PathsAndFilesSpec.hs view
@@ -0,0 +1,48 @@+module PathsAndFilesSpec where+++import GhcMod.PathsAndFiles+import GhcMod.Cradle+import qualified GhcMod.Utils as U++import Control.Monad.Trans.Maybe+import System.Directory+import System.FilePath+import Test.Hspec+import TestUtils++spec :: Spec+spec = do+ describe "getSandboxDb" $ do+ it "can parse a config file and extract the sandbox package-db" $ do+ cwd <- getCurrentDirectory+ Just crdl <- runLogDef $ runMaybeT $ plainCradle "test/data/cabal-project"+ Just db <- getSandboxDb crdl+ db `shouldSatisfy` isPkgDbAt (cwd </> "test/data/cabal-project/.cabal-sandbox")++ it "returns Nothing if the sandbox config file is broken" $ do+ Just crdl <- runLogDef $ runMaybeT $ plainCradle "test/data/broken-sandbox"+ getSandboxDb crdl `shouldReturn` Nothing++ describe "findCabalFile" $ do+ it "works" $ do+ p <- U.makeAbsolute' "test/data/cabal-project/cabalapi.cabal"+ findCabalFile "test/data/cabal-project" `shouldReturn` Just p++ it "finds cabal files in parent directories" $ do+ p <- U.makeAbsolute' "test/data/cabal-project/cabalapi.cabal"+ findCabalFile "test/data/cabal-project/subdir1/subdir2" `shouldReturn` Just p++ describe "findStackConfigFile" $ do+ it "works" $ do+ p <- U.makeAbsolute' "test/data/stack-project/stack.yaml"+ findStackConfigFile "test/data/stack-project" `shouldReturn` Just p++ describe "findCabalSandboxDir" $ do+ it "works" $ do+ p <- U.makeAbsolute' "test/data/cabal-project"+ findCabalSandboxDir "test/data/cabal-project" `shouldReturn` Just p++ it "finds sandboxes in parent directories" $ do+ p <- U.makeAbsolute' "test/data/cabal-project"+ findCabalSandboxDir "test/data/cabal-project/subdir1/subdir2" `shouldReturn` Just p
+ test/ShellParseSpec.hs view
@@ -0,0 +1,34 @@+module ShellParseSpec where+++import GhcMod.Exe.Options.ShellParse++import Test.Hspec++spec :: Spec+spec =+ describe "parseCmdLine" $ do+ it "splits arguments" $ do+ parseCmdLine "test command line" `shouldBe` ["test", "command", "line"]+ parseCmdLine "ascii-escape test command line" `shouldBe` ["test", "command", "line"]+ it "honors quoted segments if turned on" $+ parseCmdLine "ascii-escape test command line \STXwith quoted segment\ETX"+ `shouldBe` ["test", "command", "line", "with quoted segment"]+ it "doesn't honor quoted segments if turned off" $+ parseCmdLine "test command line \STXwith quoted segment\ETX"+ `shouldBe` words "test command line \STXwith quoted segment\ETX"+ it "squashes multiple spaces" $ do+ parseCmdLine "test command"+ `shouldBe` ["test", "command"]+ parseCmdLine "ascii-escape test command"+ `shouldBe` ["test", "command"]+ it "ingores leading spaces" $ do+ parseCmdLine " test command"+ `shouldBe` ["test", "command"]+ parseCmdLine " ascii-escape test command"+ `shouldBe` ["test", "command"]+ it "parses empty string as no argument" $ do+ parseCmdLine ""+ `shouldBe` [""]+ parseCmdLine "ascii-escape "+ `shouldBe` [""]
− test/Spec.hs
@@ -1,1 +0,0 @@-{-# OPTIONS_GHC -F -pgmF hspec-discover -optF --no-main #-}
+ test/TargetSpec.hs view
@@ -0,0 +1,47 @@+{-# LANGUAGE OverloadedStrings #-}+module TargetSpec where++import GhcMod.Target+import GhcMod.LightGhc+import GhcMod.Gap+import Test.Hspec++import TestUtils++import GHC+import Data.List+import Data.Maybe+import System.Directory+import System.FilePath++spec :: Spec+spec = do+ describe "runLightGhc" $ do+ it "works at all" $ do+ withLightHscEnv [] $ \env ->+ runLightGhc env (return ()) `shouldReturn` ()++ it "has modules in scope" $ do+ (withLightHscEnv [] $ \env ->+ runLightGhc env $ do+ dflags <- getSessionDynFlags+ let i = intersect (listVisibleModuleNames dflags)+ ["Control.Applicative", "Control.Arrow"+ ,"Control.Exception", "GHC.Exts", "GHC.Float"]+ liftIO $ i `shouldSatisfy` not . null) :: IO ()++ it "can get module info" $ do+ (withLightHscEnv [] $ \env ->+ runLightGhc env $ do+ mdl <- findModule "Data.List" Nothing+ mmi <- getModuleInfo mdl+ liftIO $ isJust mmi `shouldBe` True) :: IO ()+++ describe "resolveModule" $ do+ it "Works when a module given as path uses CPP" $ do+ dir <- getCurrentDirectory+ let srcDirs = [dir </> "test/data/target/src"]+ x <- withLightHscEnv [] $ \env -> runD $ do+ resolveModule env srcDirs (Left $ dir </> "test/data/target/Cpp.hs")+ liftIO $ x `shouldBe` Just (ModulePath "Cpp" $ dir </> "test/data/target/Cpp.hs")
test/TestUtils.hs view
@@ -1,27 +1,42 @@+{-# OPTIONS_GHC -fno-warn-orphans #-} module TestUtils ( run , runD , runD'- , runI- , runID- , runIsolatedGhcMod- , isolateCradle+ , runE+ , runNullLog+ , runGmOutDef+ , runLogDef , shouldReturnError- , module Language.Haskell.GhcMod.Monad- , module Language.Haskell.GhcMod.Types+ , isPkgDbAt+ , isPkgConfDAt+ , module GhcMod.Monad+ , module GhcMod.Types ) where -import Language.Haskell.GhcMod.Monad-import Language.Haskell.GhcMod.Types+import GhcMod.Logging+import GhcMod.Monad+import GhcMod.Cradle+import GhcMod.Types +import Control.Arrow+import Control.Category+import Control.Applicative+import Control.Monad.Error (ErrorT, runErrorT)+import Control.Monad.Trans.Journal+import Data.List.Split+import Data.Label+import Data.String+import System.FilePath+import System.Directory import Test.Hspec+import Prelude hiding ((.)) -isolateCradle :: IOish m => GhcModT m a -> GhcModT m a-isolateCradle action =- local modifyEnv $ action- where- modifyEnv e = e { gmCradle = (gmCradle e) { cradlePkgDbStack = [GlobalDb] } }+import Exception +testLogLevel :: GmLogLevel+testLogLevel = GmDebug+ extract :: Show e => IO (Either e a, w) -> IO a extract action = do (r,_) <- action@@ -29,29 +44,57 @@ Right a -> return a Left e -> error $ show e -runIsolatedGhcMod :: Options -> GhcModT IO a -> IO a-runIsolatedGhcMod opt action = do- extract $ runGhcModT opt $ isolateCradle action+runGhcModTSpec :: Options -> GhcModT IO a -> IO (Either GhcModError a, GhcModLog)+runGhcModTSpec opt action = do+ dir <- getCurrentDirectory+ runGhcModTSpec' dir opt action --- | Run GhcMod in isolated cradle with default options-runID :: GhcModT IO a -> IO a-runID = runIsolatedGhcMod defaultOptions+runGhcModTSpec' :: IOish m+ => FilePath -> Options -> GhcModT m b -> m (Either GhcModError b, GhcModLog)+runGhcModTSpec' dir opt action = liftIO (canonicalizePath dir) >>= \dir' -> do+ runGmOutT opt $+ withGhcModEnv' withSpecCradle dir' opt $ \(env,_) -> do+ first (fst <$>) <$> runGhcModT' env defaultGhcModState+ (gmSetLogLevel (ooptLogLevel $ optOutput opt) >> action)+ where+ withSpecCradle :: (IOish m, GmOut m) => FilePath -> ((Cradle, GhcModLog) -> m a) -> m a+ withSpecCradle cradledir f =+ gbracket+ (runJournalT $ findSpecCradle (optPrograms opt) cradledir)+ (liftIO . cleanupCradle . fst) f --- | Run GhcMod in isolated cradle-runI :: Options -> GhcModT IO a -> IO a-runI = runIsolatedGhcMod -- | Run GhcMod run :: Options -> GhcModT IO a -> IO a-run opt a = extract $ runGhcModT opt a+run opt a = extract $ runGhcModTSpec opt a -- | Run GhcMod with default options runD :: GhcModT IO a -> IO a-runD = extract . runGhcModT defaultOptions+runD =+ extract . runGhcModTSpec (setLogLevel testLogLevel defaultOptions) -runD' :: GhcModT IO a -> IO (Either GhcModError a, GhcModLog)-runD' = runGhcModT defaultOptions+runD' :: FilePath -> GhcModT IO a -> IO a+runD' dir =+ extract . runGhcModTSpec' dir (setLogLevel testLogLevel defaultOptions) +setLogLevel :: GmLogLevel -> Options -> Options+setLogLevel = set (lOoptLogLevel . lOptOutput)++runE :: ErrorT e IO a -> IO (Either e a)+runE = runErrorT++runNullLog :: MonadIO m => JournalT GhcModLog m a -> m a+runNullLog action = do+ (a,w) <- runJournalT action+ liftIO $ print w+ return a++runGmOutDef :: IOish m => GmOutT m a -> m a+runGmOutDef = runGmOutT defaultOptions++runLogDef :: IOish m => GmOutT (JournalT GhcModLog m) a -> m a+runLogDef = fmap fst . runJournalT . runGmOutDef+ shouldReturnError :: Show a => IO (Either GhcModError a, GhcModLog) -> Expectation@@ -61,3 +104,21 @@ where isLeft (Left _) = True isLeft _ = False++isPkgConfD :: FilePath -> Bool+isPkgConfD d = let+ (_dir, pkgconfd) = splitFileName d+ in case splitOn "-" pkgconfd of+ [_arch, _platform, _compiler, _compver, "packages.conf.d"] -> True+ _ -> False++isPkgConfDAt :: FilePath -> FilePath -> Bool+isPkgConfDAt d d' | d == takeDirectory d' && isPkgConfD d' = True+isPkgConfDAt _ _ = False++isPkgDbAt :: FilePath -> GhcPkgDb -> Bool+isPkgDbAt d (PackageDb dir) = isPkgConfDAt d dir+isPkgDbAt _ _ = False++instance IsString ModuleName where+ fromString = mkModuleName
− test/data/.cabal-sandbox/i386-osx-ghc-7.6.3-packages.conf.d/Cabal-1.18.1.3-2b161c6bf77657aa17e1681d83cb051b.conf
@@ -1,4 +0,0 @@-name: Cabal-version: 1.18.1.3-id: Cabal-1.18.1.3-2b161c6bf77657aa17e1681d83cb051b-exposed: True
− test/data/.cabal-sandbox/packages/00-index.tar
binary file changed (10240 → absent bytes)
− test/data/Bar.hs
@@ -1,5 +0,0 @@-{-# LANGUAGE TemplateHaskell #-}-module Bar (bar) where-import Foo (foo)--bar = $foo ++ "bar"
− test/data/Baz.hs
@@ -1,5 +0,0 @@-{-# LANGUAGE QuasiQuotes #-}-module Baz (baz) where-import Foo (fooQ)--baz = [fooQ| foo bar baz |]
− test/data/Foo.hs
@@ -1,9 +0,0 @@-module Foo (foo, fooQ) where-import Language.Haskell.TH-import Language.Haskell.TH.Quote (QuasiQuoter(..))--foo :: ExpQ-foo = stringE "foo"--fooQ :: QuasiQuoter-fooQ = QuasiQuoter (litE . stringL) undefined undefined undefined
− test/data/ForeignExport.hs
@@ -1,10 +0,0 @@-{-# LANGUAGE ForeignFunctionInterface #-}--module ForeignExport where--import Foreign.C.Types--foreign export ccall foo :: CUInt--foo :: CUInt-foo = 123
− test/data/Info.hs
@@ -1,8 +0,0 @@-{-# LANGUAGE TemplateHaskell #-} -- for HscInterpreted--module Info () where--fib :: Int -> Int-fib 0 = 0-fib 1 = 1-fib n = fib (n - 1) + fib (n - 2)
− test/data/Main.hs
@@ -1,3 +0,0 @@-import Bar (bar)--main = putStrLn bar
− test/data/Mutual1.hs
@@ -1,5 +0,0 @@-{-# LANGUAGE TemplateHaskell #-} -- for HscInterpreted--module Mutual1 where--import Mutual2
− test/data/Mutual2.hs
@@ -1,3 +0,0 @@-module Mutual2 where--import Mutual1
− test/data/Unicode.hs
@@ -1,4 +0,0 @@-module Unicode where--unicode :: α -> α-unicode = id
+ test/data/annotations/With.hs view
@@ -0,0 +1,6 @@+module Main where++{-# ANN module ["this", "can", "be", "anything"] #-}++main :: IO ()+main = putStrLn "Hello world!"
− test/data/broken-cabal/cabal.sandbox.config.in
@@ -1,25 +0,0 @@--- This is a Cabal package environment file.--- THIS FILE IS AUTO-GENERATED. DO NOT EDIT DIRECTLY.--- Please create a 'cabal.config' file in the same directory--- if you want to change the default settings for this sandbox.---local-repo: @CWD@/test/data/broken-cabal/.cabal-sandbox/packages-logs-dir: @CWD@/test/data/broken-cabal/.cabal-sandbox/logs-world-file: @CWD@/test/data/broken-cabal/.cabal-sandbox/world-user-install: False-package-db: @CWD@/test/data/broken-cabal/.cabal-sandbox/x86_64-linux-ghc-7.8.3-packages.conf.d-build-summary: @CWD@/test/data/broken-cabal/.cabal-sandbox/logs/build.log--install-dirs- prefix: @CWD@/test/data/broken-cabal/.cabal-sandbox- bindir: $prefix/bin- libdir: $prefix/lib- libsubdir: $arch-$os-$compiler/$pkgid- libexecdir: $prefix/libexec- datadir: $prefix/share- datasubdir: $arch-$os-$compiler/$pkgid- docdir: $datadir/doc/$arch-$os-$compiler/$pkgid- htmldir: $docdir/html- haddockdir: $htmldir- sysconfdir: $prefix/etc
test/data/cabal-flags/cabal-flags.cabal view
@@ -7,8 +7,7 @@ default: False library- build-depends: base == 4.*+ build-depends: base if flag(test-flag) build-depends: Cabal >= 1.10-
+ test/data/cabal-preprocessors/Main.hs view
@@ -0,0 +1,4 @@+import Preprocessed++main :: IO ()+main = return warning
+ test/data/cabal-preprocessors/Preprocessed.hsc view
@@ -0,0 +1,3 @@+module Preprocessed where++warning = ()
+ test/data/cabal-preprocessors/cabal-preprocessors.cabal view
@@ -0,0 +1,14 @@+name: cabal-preprocessors+version: 0.1.0.0+license-file: LICENSE+author: asd+maintainer: asd+build-type: Simple+cabal-version: >=1.10++executable cabal-preprocessors+ main-is: Main.hs+ build-depends: base+ default-language: Haskell2010+ other-modules: Preprocessed+ ghc-options: -Wall
+ test/data/cabal-project/.cabal-sandbox/i386-osx-ghc-7.6.3-packages.conf.d/Cabal-1.18.1.3-2b161c6bf77657aa17e1681d83cb051b.conf view
@@ -0,0 +1,4 @@+name: Cabal+version: 1.18.1.3+id: Cabal-1.18.1.3-2b161c6bf77657aa17e1681d83cb051b+exposed: True
+ test/data/cabal-project/Baz.hs view
@@ -0,0 +1,5 @@+{-# LANGUAGE QuasiQuotes #-}+module Baz (baz) where+import Foo (fooQ)++baz = [fooQ| foo bar baz |]
+ test/data/cabal-project/Foo.hs view
@@ -0,0 +1,9 @@+module Foo (foo, fooQ) where+import Language.Haskell.TH+import Language.Haskell.TH.Quote (QuasiQuoter(..))++foo :: ExpQ+foo = stringE "foo"++fooQ :: QuasiQuoter+fooQ = QuasiQuoter (litE . stringL) undefined undefined undefined
+ test/data/cabal-project/Info.hs view
@@ -0,0 +1,8 @@+{-# LANGUAGE TemplateHaskell #-} -- for HscInterpreted++module Info () where++fib :: Int -> Int+fib 0 = 0+fib 1 = 1+fib n = fib (n - 1) + fib (n - 2)
+ test/data/cabal-project/Main.hs view
@@ -0,0 +1,3 @@+import Bar (bar)++main = putStrLn bar
+ test/data/cabal-project/cabalapi.cabal view
@@ -0,0 +1,67 @@+Name: ghc-mod+Version: 1.11.3+Author: Kazu Yamamoto <kazu@iij.ad.jp>+Maintainer: Kazu Yamamoto <kazu@iij.ad.jp>+License: BSD3+License-File: LICENSE+Homepage: http://www.mew.org/~kazu/proj/ghc-mod/+Synopsis: Happy Haskell programming on Emacs/Vim+Description: This packages includes Elisp files+ and a Haskell command, "ghc-mod".+ "ghc*.el" enable completion of+ Haskell symbols on Emacs.+ Flymake is also integrated.+ "ghc-mod" is a backend of "ghc*.el".+ It lists up all installed modules+ or extracts names of functions, classes,+ and data declarations.+ To use "ghc-mod" on Vim,+ see <https://github.com/eagletmt/ghcmod-vim> or+ <https://github.com/scrooloose/syntastic>+Category: Development+Cabal-Version: >= 1.6+Build-Type: Simple+Data-Dir: elisp+Data-Files: Makefile ghc.el ghc-func.el ghc-doc.el ghc-comp.el+ ghc-flymake.el ghc-command.el ghc-info.el+ ghc-ins-mod.el ghc-indent.el+Executable ghc-mod+ Main-Is: GHCMod.hs+ Other-Modules: Browse+ CabalApi+ Cabal+ CabalDev+ Check+ ErrMsg+ Flag+ GHCApi+ GHCChoice+ Gap+ Info+ Lang+ Lint+ List+ Paths_ghc_mod+ Types+ GHC-Options: -Wall+ Build-Depends: base+ , Cabal >= 1.10+ , template-haskell++Test-Suite spec+ Main-Is: Spec.hs+ Hs-Source-Dirs: test, .+ Type: exitcode-stdio-1.0+ Other-Modules: Expectation+ BrowseSpec+ CabalApiSpec+ FlagSpec+ LangSpec+ LintSpec+ ListSpec+ Build-Depends: base+ , Cabal >= 1.10++Source-Repository head+ Type: git+ Location: git://github.com/kazu-yamamoto/ghc-mod.git
+ test/data/cabal-project/subdir1/subdir2/dummy view
@@ -0,0 +1,1 @@+dummy
− test/data/cabal.sandbox.config.in
@@ -1,25 +0,0 @@--- This is a Cabal package environment file.--- THIS FILE IS AUTO-GENERATED. DO NOT EDIT DIRECTLY.--- Please create a 'cabal.config' file in the same directory--- if you want to change the default settings for this sandbox.---local-repo: @CWD@/test/data/.cabal-sandbox/packages-logs-dir: @CWD@/test/data/.cabal-sandbox/logs-world-file: @CWD@/test/data/.cabal-sandbox/world-user-install: False-package-db: @CWD@/test/data/.cabal-sandbox/i386-osx-ghc-7.6.3-packages.conf.d-build-summary: @CWD@/test/data/.cabal-sandbox/logs/build.log--install-dirs- prefix: @CWD@/test/data/.cabal-sandbox- bindir: $prefix/bin- libdir: $prefix/lib- libsubdir: $arch-$os-$compiler/$pkgid- libexecdir: $prefix/libexec- datadir: $prefix/share- datasubdir: $arch-$os-$compiler/$pkgid- docdir: $datadir/doc/$arch-$os-$compiler/$pkgid- htmldir: $docdir/html- haddockdir: $htmldir- sysconfdir: $prefix/etc
− test/data/cabalapi.cabal
@@ -1,67 +0,0 @@-Name: ghc-mod-Version: 1.11.3-Author: Kazu Yamamoto <kazu@iij.ad.jp>-Maintainer: Kazu Yamamoto <kazu@iij.ad.jp>-License: BSD3-License-File: LICENSE-Homepage: http://www.mew.org/~kazu/proj/ghc-mod/-Synopsis: Happy Haskell programming on Emacs/Vim-Description: This packages includes Elisp files- and a Haskell command, "ghc-mod".- "ghc*.el" enable completion of- Haskell symbols on Emacs.- Flymake is also integrated.- "ghc-mod" is a backend of "ghc*.el".- It lists up all installed modules- or extracts names of functions, classes,- and data declarations.- To use "ghc-mod" on Vim,- see <https://github.com/eagletmt/ghcmod-vim> or- <https://github.com/scrooloose/syntastic>-Category: Development-Cabal-Version: >= 1.6-Build-Type: Simple-Data-Dir: elisp-Data-Files: Makefile ghc.el ghc-func.el ghc-doc.el ghc-comp.el- ghc-flymake.el ghc-command.el ghc-info.el- ghc-ins-mod.el ghc-indent.el-Executable ghc-mod- Main-Is: GHCMod.hs- Other-Modules: Browse- CabalApi- Cabal- CabalDev- Check- ErrMsg- Flag- GHCApi- GHCChoice- Gap- Info- Lang- Lint- List- Paths_ghc_mod- Types- GHC-Options: -Wall- Build-Depends: base >= 4.0 && < 5- , Cabal >= 1.10- , template-haskell--Test-Suite spec- Main-Is: Spec.hs- Hs-Source-Dirs: test, .- Type: exitcode-stdio-1.0- Other-Modules: Expectation- BrowseSpec- CabalApiSpec- FlagSpec- LangSpec- LintSpec- ListSpec- Build-Depends: base >= 4.0 && < 5- , Cabal >= 1.10--Source-Repository head- Type: git- Location: git://github.com/kazu-yamamoto/ghc-mod.git
+ test/data/case-split/Crash.hs view
@@ -0,0 +1,4 @@+module Crash where++test :: Maybe a+test x = undefined
+ test/data/case-split/Vect.hs view
@@ -0,0 +1,39 @@+{-# LANGUAGE DataKinds, TypeFamilies, TypeOperators, GADTs, KindSignatures #-}++module Vect where++data Nat = Z | S Nat++type family (n :: Nat) :+ (m :: Nat) :: Nat+type instance Z :+ m = m+type instance S n :+ m = S (n :+ m)++data Vect :: Nat -> * -> * where+ VNil :: Vect Z a+ (:::) :: a -> Vect n a -> Vect (S n) a++vAppend :: Vect n a -> Vect m a -> Vect (n :+ m) a+vAppend x y = _vAppend_body++lAppend :: [a] -> [a] -> [a]+lAppend x y = _lAppend_body++data MyList a = Nil | Cons a (MyList a)++mlAppend :: MyList a -> MyList a -> MyList a+mlAppend x y = _mlAppend_body++mlAppend2 :: MyList a -> MyList a -> MyList a+mlAppend2 x y = case x of+ x' -> _mlAppend_body++mlReverse :: MyList a -> MyList a+mlReverse xs = mlReverse' xs Nil+ where+ mlReverse' :: MyList a -> MyList a -> MyList a+ mlReverse' xs' accum = _mlReverse_body++mlReverse2 :: MyList a -> MyList a+mlReverse2 xs = let mlReverse' :: MyList a -> MyList a -> MyList a+ mlReverse' xs' accum = _mlReverse_body+ in mlReverse' xs Nil
+ test/data/case-split/Vect706.hs view
@@ -0,0 +1,39 @@+{-# LANGUAGE DataKinds, TypeFamilies, TypeOperators, GADTs, KindSignatures #-}++module Vect706 where++data Nat = Z | S Nat++type family (n :: Nat) :+ (m :: Nat) :: Nat+type instance Z :+ m = m+type instance S n :+ m = S (n :+ m)++data Vect :: Nat -> * -> * where+ VNil :: Vect Z a+ (:::) :: a -> Vect n a -> Vect (S n) a++vAppend :: Vect n a -> Vect m a -> Vect (n :+ m) a+vAppend x y = undefined++lAppend :: [a] -> [a] -> [a]+lAppend x y = undefined++data MyList a = Nil | Cons a (MyList a)++mlAppend :: MyList a -> MyList a -> MyList a+mlAppend x y = undefined++mlAppend2 :: MyList a -> MyList a -> MyList a+mlAppend2 x y = case x of+ x' -> undefined++mlReverse :: MyList a -> MyList a+mlReverse xs = mlReverse' xs Nil+ where+ mlReverse' :: MyList a -> MyList a -> MyList a+ mlReverse' xs' accum = undefined++mlReverse2 :: MyList a -> MyList a+mlReverse2 xs = let mlReverse' :: MyList a -> MyList a -> MyList a+ mlReverse' xs' accum = undefined+ in mlReverse' xs Nil
+ test/data/check-missing-warnings/DesugarWarnings.hs view
@@ -0,0 +1,5 @@+module Warnings (zoo) where++zoo :: [a] -> ()+zoo x = case x of+ [] -> undefined
− test/data/check-packageid/cabal.sandbox.config.in
@@ -1,25 +0,0 @@--- This is a Cabal package environment file.--- THIS FILE IS AUTO-GENERATED. DO NOT EDIT DIRECTLY.--- Please create a 'cabal.config' file in the same directory--- if you want to change the default settings for this sandbox.---local-repo: @CWD@/test/data/check-packageid/.cabal-sandbox/packages-logs-dir: @CWD@/test/data/check-packageid/.cabal-sandbox/logs-world-file: @CWD@/test/data/check-packageid/.cabal-sandbox/world-user-install: False-package-db: @CWD@/test/data/check-packageid/.cabal-sandbox/i386-osx-ghc-7.6.3-packages.conf.d-build-summary: @CWD@/test/data/check-packageid/.cabal-sandbox/logs/build.log--install-dirs- prefix: @CWD@/test/data/check-packageid/.cabal-sandbox- bindir: $prefix/bin- libdir: $prefix/lib- libsubdir: $arch-$os-$compiler/$pkgid- libexecdir: $prefix/libexec- datadir: $prefix/share- datasubdir: $arch-$os-$compiler/$pkgid- docdir: $datadir/doc/$arch-$os-$compiler/$pkgid- htmldir: $docdir/html- haddockdir: $htmldir- sysconfdir: $prefix/etc
test/data/check-test-subdir/check-test-subdir.cabal view
@@ -13,3 +13,4 @@ build-depends: base == 4.* hs-source-dirs: test main-is: Main.hs+ ghc-options: -Wall
+ test/data/custom-cradle/custom-cradle.cabal view
@@ -0,0 +1,12 @@+name: custom-cradle+version: 0.1.0.0+homepage: asd+license-file: LICENSE+author: asd+maintainer: asd+build-type: Simple+cabal-version: >=1.10++library+ build-depends: base+ default-language: Haskell2010
+ test/data/custom-cradle/ghc-mod.package-db-stack view
@@ -0,0 +1,5 @@+global+user+package-db-a+package-db-b+package-db-c
+ test/data/custom-cradle/package-db-a/.gitkeep view
+ test/data/custom-cradle/package-db-b/.gitkeep view
+ test/data/custom-cradle/package-db-c/.gitkeep view
− test/data/duplicate-pkgver/cabal.sandbox.config.in
@@ -1,25 +0,0 @@--- This is a Cabal package environment file.--- THIS FILE IS AUTO-GENERATED. DO NOT EDIT DIRECTLY.--- Please create a 'cabal.config' file in the same directory--- if you want to change the default settings for this sandbox.---local-repo: @CWD@/test/data/duplicate-pkgver/.cabal-sandbox/packages-logs-dir: @CWD@/test/data/duplicate-pkgver/.cabal-sandbox/logs-world-file: @CWD@/test/data/duplicate-pkgver/.cabal-sandbox/world-user-install: False-package-db: @CWD@/test/data/duplicate-pkgver/.cabal-sandbox/i386-osx-ghc-7.6.3-packages.conf.d-build-summary: @CWD@/test/data/duplicate-pkgver/.cabal-sandbox/logs/build.log--install-dirs- prefix: @CWD@/test/data/duplicate-pkgver/.cabal-sandbox- bindir: $prefix/bin- libdir: $prefix/lib- libsubdir: $arch-$os-$compiler/$pkgid- libexecdir: $prefix/libexec- datadir: $prefix/share- datasubdir: $arch-$os-$compiler/$pkgid- docdir: $datadir/doc/$arch-$os-$compiler/$pkgid- htmldir: $docdir/html- haddockdir: $htmldir- sysconfdir: $prefix/etc
+ test/data/file-mapping/File.hs view
@@ -0,0 +1,2 @@+main :: IO ()+main = putStrLn "Hello World!"
+ test/data/file-mapping/File_Redir.hs view
@@ -0,0 +1,1 @@+main = putStrLn "Hello World!"
+ test/data/file-mapping/File_Redir_Lint.hs view
@@ -0,0 +1,4 @@+module File where++func :: Num a => a -> a -> a+func a b = (*) a b
+ test/data/file-mapping/lhs/File.lhs view
@@ -0,0 +1,2 @@+> main :: IO ()+> main = putStrLn "Hello World!"
+ test/data/file-mapping/lhs/File_Redir.lhs view
@@ -0,0 +1,1 @@+> main = putStrLn "Hello World!"
+ test/data/file-mapping/lhs/File_Redir_Lint.lhs view
@@ -0,0 +1,4 @@+> module File where++> func :: Num a => a -> a -> a+> func a b = (*) a b
+ test/data/file-mapping/preprocessor/File.hs view
@@ -0,0 +1,7 @@+{-# LANGUAGE CPP #-}+#ifndef NOTHING+main :: IO ()+main = putStrLn "Hello World!"+#else+INVALID+#endif
+ test/data/file-mapping/preprocessor/File_Redir.hs view
@@ -0,0 +1,6 @@+{-# LANGUAGE CPP #-}+#ifndef NOTHING+main = putStrLn "Hello World!"+#else+INVALID+#endif
+ test/data/file-mapping/preprocessor/File_Redir_Lint.hs view
@@ -0,0 +1,9 @@+{-# LANGUAGE CPP #-}+#ifndef NOTHING+module File where++func :: Num a => a -> a -> a+func a b = (*) a b+#else+INVALID+#endif
+ test/data/foreign-export/ForeignExport.hs view
@@ -0,0 +1,10 @@+{-# LANGUAGE ForeignFunctionInterface #-}++module ForeignExport where++import Foreign.C.Types++foreign export ccall foo :: CUInt++foo :: CUInt+foo = 123
− test/data/ghc-mod-check/Data/Foo.hs
@@ -1,11 +0,0 @@-module Data.Foo where--foo :: Int-foo = undefined--fibonacci :: Int -> Integer-fibonacci n = fib 1 0 1- where- fib m x y- | n == m = y- | otherwise = fib (m+1) y (x + y)
test/data/ghc-mod-check/ghc-mod-check.cabal view
@@ -15,13 +15,12 @@ cabal-version: >=1.8 library- -- exposed-modules:- -- other-modules:+ HS-Source-Dirs: lib build-depends: base exposed-modules: Data.Foo executable foo Main-Is: main.hs GHC-Options: -Wall- Build-Depends: base >= 4 && < 5+ Build-Depends: base , ghc-mod-check
+ test/data/ghc-mod-check/lib/Data/Foo.hs view
@@ -0,0 +1,11 @@+module Data.Foo where++foo :: Int+foo = undefined++fibonacci :: Int -> Integer+fibonacci n = fib 1 0 1+ where+ fib m x y+ | n == m = y+ | otherwise = fib (m+1) y (x + y)
− test/data/hlint.hs
@@ -1,5 +0,0 @@-module Hlist where--main :: IO ()-main = do- putStrLn "Hello, world!"
+ test/data/hlint/hlint.hs view
@@ -0,0 +1,5 @@+module Hlist where++main :: IO ()+main = do+ putStrLn "Hello, world!"
+ test/data/home-module-graph/cpp/A.hs view
@@ -0,0 +1,4 @@+module A where+import A1+import A2+import A3
+ test/data/home-module-graph/cpp/A1.hs view
@@ -0,0 +1,4 @@+{-# LANGUAGE CPP #-}+module A1 where+#elif+import B
+ test/data/home-module-graph/cpp/A2.hs view
@@ -0,0 +1,1 @@+module A2 where
+ test/data/home-module-graph/cpp/A3.hs view
@@ -0,0 +1,2 @@+module A3 where+import B
+ test/data/home-module-graph/cpp/B.hs view
@@ -0,0 +1,1 @@+module B where
+ test/data/home-module-graph/cycle/A.hs view
@@ -0,0 +1,2 @@+module A where+import B
+ test/data/home-module-graph/cycle/B.hs view
@@ -0,0 +1,2 @@+module B where+import A
+ test/data/home-module-graph/errors/A.hs view
@@ -0,0 +1,4 @@+module A where+import A1+import A2+import A3
+ test/data/home-module-graph/errors/A1.hs view
@@ -0,0 +1,4 @@+module A1 where+psogduapzs9+import B+lx,vLMCks
+ test/data/home-module-graph/errors/A2.hs view
@@ -0,0 +1,1 @@+module A2 where
+ test/data/home-module-graph/errors/A3.hs view
@@ -0,0 +1,2 @@+module A3 where+import B
+ test/data/home-module-graph/errors/B.hs view
@@ -0,0 +1,1 @@+module B where
+ test/data/home-module-graph/indirect-update/A.hs view
@@ -0,0 +1,4 @@+module A where+import A1+import A2+import A3
+ test/data/home-module-graph/indirect-update/A1.hs view
@@ -0,0 +1,2 @@+module A1 where+import B
+ test/data/home-module-graph/indirect-update/A2.hs view
@@ -0,0 +1,1 @@+module A2 where
+ test/data/home-module-graph/indirect-update/A3.hs view
@@ -0,0 +1,2 @@+module A3 where+import B
+ test/data/home-module-graph/indirect-update/B.hs view
@@ -0,0 +1,1 @@+module B where
+ test/data/home-module-graph/indirect-update/C.hs view
@@ -0,0 +1,1 @@+module C where
+ test/data/home-module-graph/indirect/A.hs view
@@ -0,0 +1,4 @@+module A where+import A1+import A2+import A3
+ test/data/home-module-graph/indirect/A1.hs view
@@ -0,0 +1,2 @@+module A1 where+import B
+ test/data/home-module-graph/indirect/A2.hs view
@@ -0,0 +1,2 @@+module A2 where+import C
+ test/data/home-module-graph/indirect/A3.hs view
@@ -0,0 +1,2 @@+module A3 where+import B
+ test/data/home-module-graph/indirect/B.hs view
@@ -0,0 +1,1 @@+module B where
+ test/data/home-module-graph/indirect/C.hs view
@@ -0,0 +1,1 @@+module C where
+ test/data/import-cycle/Mutual1.hs view
@@ -0,0 +1,5 @@+{-# LANGUAGE TemplateHaskell #-} -- for HscInterpreted++module Mutual1 where++import Mutual2
+ test/data/import-cycle/Mutual2.hs view
@@ -0,0 +1,3 @@+module Mutual2 where++import Mutual1
+ test/data/nice-qualification/NiceQualification.hs view
@@ -0,0 +1,4 @@+module Main where++main :: IO ()+main = "wrong type"
+ test/data/non-exported/Fib.hs view
@@ -0,0 +1,8 @@+{-# LANGUAGE TemplateHaskell #-} -- for HscInterpreted++module Fib () where++fib :: Int -> Int+fib 0 = 0+fib 1 = 1+fib n = fib (n - 1) + fib (n - 2)
test/data/pattern-synonyms/A.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE PatternSynonyms #-}+ module A where data SomeType a b = SomeType (a,b)
test/data/pattern-synonyms/pattern-synonyms.cabal view
@@ -1,24 +1,27 @@--- Initial pattern-synonyms.cabal generated by cabal init. For further +-- Initial pattern-synonyms.cabal generated by cabal init. For further -- documentation, see http://haskell.org/cabal/users-guide/ name: pattern-synonyms version: 0.1.0.0--- synopsis: --- description: --- license: +-- synopsis:+-- description:+-- license: license-file: LICENSE author: Daniel Gröber maintainer: dxld@darkboxed.org--- copyright: --- category: +-- copyright:+-- category: build-type: Simple--- extra-source-files: +-- extra-source-files: cabal-version: >=1.10 library exposed-modules: A, B- -- other-modules: + -- other-modules: other-extensions: PatternSynonyms- build-depends: base >=4.7 && <4.8- -- hs-source-dirs: + build-depends: base+ -- hs-source-dirs: default-language: Haskell2010+ ghc-options: -Wall+ if impl(ghc >= 8.0.1)+ ghc-options: -Wno-missing-pattern-synonym-signatures
+ test/data/quasi-quotes/FooQ.hs view
@@ -0,0 +1,6 @@+module FooQ (fooQ) where+import Language.Haskell.TH+import Language.Haskell.TH.Quote (QuasiQuoter(..))++fooQ :: QuasiQuoter+fooQ = QuasiQuoter (litE . stringL) undefined undefined undefined
+ test/data/quasi-quotes/QuasiQuotes.hs view
@@ -0,0 +1,6 @@+{-# LANGUAGE QuasiQuotes #-}+module QuasiQuotes where++import FooQ++bar = [fooQ| foo bar baz |]
+ test/data/stack-project/Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ test/data/stack-project/app/Main.hs view
@@ -0,0 +1,6 @@+module Main where++import Lib++main :: IO ()+main = someFunc
+ test/data/stack-project/new-template.cabal view
@@ -0,0 +1,42 @@+name: new-template+version: 0.1.0.0+synopsis: Initial project template from stack+description: Please see README.md+homepage: http://github.com/name/project+-- license: BSD3+-- license-file: LICENSE+author: Your name here+maintainer: your.address@example.com+-- copyright:+category: Web+build-type: Simple+-- extra-source-files:+cabal-version: >=1.10++library+ hs-source-dirs: src+ exposed-modules: Lib+ build-depends: base+ default-language: Haskell2010++executable new-template-exe+ hs-source-dirs: app+ main-is: Main.hs+ ghc-options: -threaded -rtsopts -with-rtsopts=-N+ build-depends: base+ , new-template+ , bytestring+ default-language: Haskell2010++test-suite new-template-test+ type: exitcode-stdio-1.0+ hs-source-dirs: test+ main-is: Spec.hs+ build-depends: base+ , new-template+ ghc-options: -threaded -rtsopts -with-rtsopts=-N+ default-language: Haskell2010++source-repository head+ type: git+ location: https://github.com/name/project
+ test/data/stack-project/src/Lib.hs view
@@ -0,0 +1,6 @@+module Lib+ ( someFunc+ ) where++someFunc :: IO ()+someFunc = putStrLn "someFunc"
+ test/data/stack-project/stack.yaml.in view
@@ -0,0 +1,4 @@+flags: {}+packages:+- '.'+extra-deps: []
+ test/data/stack-project/test/Spec.hs view
@@ -0,0 +1,2 @@+main :: IO ()+main = putStrLn "Test suite not yet implemented"
− test/data/subdir1/subdir2/dummy
@@ -1,1 +0,0 @@-dummy
+ test/data/target/Cpp.hs view
@@ -0,0 +1,7 @@+{-# LANGUAGE CPP #-}+#undef NOTHING+#ifdef NOTHING+module WRONG_MODULE where+#else+module Cpp where+#endif
+ test/data/template-haskell/Bar.hs view
@@ -0,0 +1,5 @@+{-# LANGUAGE TemplateHaskell #-}+module Bar (bar) where+import Foo (foo)++bar = $foo ++ "bar"
+ test/data/template-haskell/Foo.hs view
@@ -0,0 +1,9 @@+module Foo (foo, fooQ) where+import Language.Haskell.TH+import Language.Haskell.TH.Quote (QuasiQuoter(..))++foo :: ExpQ+foo = stringE "foo"++fooQ :: QuasiQuoter+fooQ = QuasiQuoter (litE . stringL) undefined undefined undefined
+ test/data/template-haskell/ImportsTH.hs view
@@ -0,0 +1,3 @@+import Bar (bar)++main = putStrLn bar
test/doctests.hs view
@@ -1,14 +1,23 @@+{-# LANGUAGE CPP #-} module Main where import Test.DocTest+import System.Environment+import Data.Maybe main :: IO ()-main = doctest [- "-package"- , "ghc"- , "-XConstraintKinds", "-XFlexibleContexts"- , "-idist/build/autogen/"- , "-optP-include"- , "-optPdist/build/autogen/cabal_macros.h"- , "Language/Haskell/GhcMod.hs"- ]+main = do+ distdir <- (fromMaybe "dist" . lookup "DOCTEST_DIST_DIR") `fmap` getEnvironment+ doctest+ [ "-package", "ghc-" ++ VERSION_ghc+ , "-package", "transformers-" ++ VERSION_transformers+ , "-package", "mtl-" ++ VERSION_mtl+ , "-package", "directory-" ++ VERSION_directory+ , "-XScopedTypeVariables", "-XRecordWildCards", "-XNamedFieldPuns", "-XConstraintKinds", "-XFlexibleContexts", "-XDataKinds", "-XKindSignatures", "-XTypeOperators", "-XViewPatterns"+ , "-i" ++ distdir ++ "/build/autogen/"+ , "-icore/"+ , "-ishared"+-- , "-optP-include"+-- , "-optP" ++ distdir ++ "/build/autogen/cabal_macros.h"+ , "GhcMod.hs"+ ]