From e5fe94a0d2a3c728f91d8783df8af56809f92b9f Mon Sep 17 00:00:00 2001 From: Lei Zhu Date: Mon, 6 Jun 2022 14:49:03 +0800 Subject: [PATCH 01/11] init hls-explicit-fixity-plugin --- .github/workflows/test.yml | 4 + cabal.project | 1 + docs/features.md | 6 + exe/Plugins.hs | 9 + ghcide/src/Development/IDE/GHC/Compat/Core.hs | 10 +- haskell-language-server.cabal | 11 + plugins/hls-explicit-fixity-plugin/LICENSE | 201 ++++++++++++++++++ plugins/hls-explicit-fixity-plugin/README.md | 13 ++ .../hls-explicit-fixity-plugin/fixity1.png | Bin 0 -> 16425 bytes .../hls-explicit-fixity-plugin/fixity2.png | Bin 0 -> 18908 bytes .../hls-explicit-fixity-plugin.cabal | 60 ++++++ .../src/Ide/Plugin/ExplicitFixity.hs | 90 ++++++++ .../hls-explicit-fixity-plugin/test/Main.hs | 61 ++++++ .../test/testdata/Hover.hs | 40 ++++ stack-lts16.yaml | 1 + stack-lts19.yaml | 1 + stack.yaml | 1 + 17 files changed, 507 insertions(+), 2 deletions(-) create mode 100644 plugins/hls-explicit-fixity-plugin/LICENSE create mode 100644 plugins/hls-explicit-fixity-plugin/README.md create mode 100644 plugins/hls-explicit-fixity-plugin/fixity1.png create mode 100644 plugins/hls-explicit-fixity-plugin/fixity2.png create mode 100644 plugins/hls-explicit-fixity-plugin/hls-explicit-fixity-plugin.cabal create mode 100644 plugins/hls-explicit-fixity-plugin/src/Ide/Plugin/ExplicitFixity.hs create mode 100644 plugins/hls-explicit-fixity-plugin/test/Main.hs create mode 100644 plugins/hls-explicit-fixity-plugin/test/testdata/Hover.hs diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 249e0de15d..6459615eea 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -250,6 +250,10 @@ jobs: name: Test hls-gadt-plugin test suit run: cabal test hls-gadt-plugin --test-options="$TEST_OPTS" || cabal test hls-gadt-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-gadt-plugin --test-options="$TEST_OPTS" + - if: matrix.test + name: Test hls-explicit-fixity-plugin test suite + run: cabal test hls-explicit-fixity-plugin --test-options="$TEST_OPTS" || cabal test hls-explicit-fixity-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-explicit-fixity-plugin --test-options="$TEST_OPTS" + test_post_job: if: always() runs-on: ubuntu-latest diff --git a/cabal.project b/cabal.project index 6a392d90ef..9fcd6141be 100644 --- a/cabal.project +++ b/cabal.project @@ -29,6 +29,7 @@ packages: ./plugins/hls-selection-range-plugin ./plugins/hls-change-type-signature-plugin ./plugins/hls-gadt-plugin + ./plugins/hls-explicit-fixity-plugin -- Standard location for temporary packages needed for particular environments -- For example it is used in the project gitlab mirror to help in the MAcOS M1 build script diff --git a/docs/features.md b/docs/features.md index 0bf1d16487..58385e1775 100644 --- a/docs/features.md +++ b/docs/features.md @@ -44,6 +44,12 @@ Provided by: `ghcide` Type information and documentation on hover, [including from local definitions](./configuration.md#how-to-show-local-documentation-on-hover). +### Show fixity + +Provided by: `hls-explicit-fixity-plugin` + +Provides fixity information. + ## Jump to definition Provided by: `ghcide` diff --git a/exe/Plugins.hs b/exe/Plugins.hs index 5e7bb29ca1..2bc59a442f 100644 --- a/exe/Plugins.hs +++ b/exe/Plugins.hs @@ -86,6 +86,11 @@ import Ide.Plugin.ChangeTypeSignature as ChangeTypeSignature #if gadt import Ide.Plugin.GADT as GADT #endif + +#if explicitFixity +import Ide.Plugin.ExplicitFixity as ExplicitFixity +#endif + -- formatters #if floskell @@ -201,7 +206,11 @@ idePlugins recorder includeExamples = pluginDescToIdePlugins allPlugins -- The ghcide descriptors should come last so that the notification handlers -- (which restart the Shake build) run after everything else GhcIde.descriptors pluginRecorder +#if explicitFixity + ++ [ExplicitFixity.descriptor] +#endif examplePlugins = [Example.descriptor pluginRecorder "eg" ,Example2.descriptor pluginRecorder "eg2" ] + diff --git a/ghcide/src/Development/IDE/GHC/Compat/Core.hs b/ghcide/src/Development/IDE/GHC/Compat/Core.hs index 9d4cf17e6f..8bae27e7be 100644 --- a/ghcide/src/Development/IDE/GHC/Compat/Core.hs +++ b/ghcide/src/Development/IDE/GHC/Compat/Core.hs @@ -102,6 +102,10 @@ module Development.IDE.GHC.Compat.Core ( #endif -- * Fixity LexicalFixity(..), + Fixity (..), + mi_fix, + defaultFixity, + lookupFixityRn, -- * ModSummary ModSummary(..), -- * HomeModInfo @@ -551,6 +555,7 @@ import GHC.Runtime.Context (InteractiveImport (..)) import GHC.Parser.Lexer import qualified GHC.Runtime.Linker as Linker #endif +import GHC.Rename.Fixity (lookupFixityRn) import GHC.Rename.Names import GHC.Rename.Splice import qualified GHC.Runtime.Interpreter as GHCi @@ -567,7 +572,7 @@ import GHC.Tc.Utils.TcType as TcType import qualified GHC.Types.Avail as Avail #if MIN_VERSION_ghc(9,2,0) import GHC.Types.Avail (greNamePrintableName) -import GHC.Types.Fixity (LexicalFixity (..)) +import GHC.Types.Fixity (LexicalFixity (..), Fixity (..), defaultFixity) #endif #if MIN_VERSION_ghc(9,2,0) import GHC.Types.Meta @@ -612,7 +617,7 @@ import GHC.Unit.Module.Imported import GHC.Unit.Module.ModDetails import GHC.Unit.Module.ModGuts import GHC.Unit.Module.ModIface (IfaceExport, ModIface (..), - ModIface_ (..)) + ModIface_ (..), mi_fix) import GHC.Unit.Module.ModSummary (ModSummary (..)) #endif import GHC.Unit.State (ModuleOrigin (..)) @@ -687,6 +692,7 @@ import qualified Panic as Plain #endif import Parser import PatSyn +import RnFixity #if MIN_VERSION_ghc(8,8,0) import Plugins #endif diff --git a/haskell-language-server.cabal b/haskell-language-server.cabal index 49af0c5c55..7019ea1057 100644 --- a/haskell-language-server.cabal +++ b/haskell-language-server.cabal @@ -191,6 +191,11 @@ flag gadt default: True manual: True +flag explicitFixity + description: Enable explicitFixity plugin + default: True + manual: True + -- formatters flag floskell @@ -318,6 +323,11 @@ common gadt build-depends: hls-gadt-plugin ^>= 1.0 cpp-options: -Dgadt +common explicitFixity + if flag(explicitFixity) + build-depends: hls-explicit-fixity-plugin ^>= 1.0 + cpp-options: -DexplicitFixity + -- formatters common floskell @@ -370,6 +380,7 @@ executable haskell-language-server , qualifyImportedNames , selectionRange , gadt + , explicitFixity , floskell , fourmolu , ormolu diff --git a/plugins/hls-explicit-fixity-plugin/LICENSE b/plugins/hls-explicit-fixity-plugin/LICENSE new file mode 100644 index 0000000000..261eeb9e9f --- /dev/null +++ b/plugins/hls-explicit-fixity-plugin/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/hls-explicit-fixity-plugin/README.md b/plugins/hls-explicit-fixity-plugin/README.md new file mode 100644 index 0000000000..409ff7f3dc --- /dev/null +++ b/plugins/hls-explicit-fixity-plugin/README.md @@ -0,0 +1,13 @@ +# Explicit Fixity Plugin + +The hls-explicit-fixity-plugin will show available fixity explicitly while hovering. + +## Demo + +![fixity1](./fixity1.png) + +![fixity2](./fixity2.png) + +## Change log +### 1.0.0.0 +- Released! diff --git a/plugins/hls-explicit-fixity-plugin/fixity1.png b/plugins/hls-explicit-fixity-plugin/fixity1.png new file mode 100644 index 0000000000000000000000000000000000000000..0f88fd53d59cc1a99f1233840d409318128cd597 GIT binary patch literal 16425 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 literal 0 HcmV?d00001 diff --git a/plugins/hls-explicit-fixity-plugin/fixity2.png b/plugins/hls-explicit-fixity-plugin/fixity2.png new file mode 100644 index 0000000000000000000000000000000000000000..7798192a3964ab8efb44b05f0cce69ebf915db8a GIT binary patch literal 18908 zcmc$`Wl&sE*S1OU;OWJKuW#NE9r z3=9>_cPVi-&y3SGbUih*PNWM!BnDzoZa^xo3K89>JsU7lt+0AvFFK#SwS7X`o=!?qfx#y$UI1=zRNpT|01U0zcNy@Ri#cKnv3Uw1B2skM z`F=31#dyZPz`lUX+r)~19-s40I6z!VN~+d1)1!(C(VK>bhBy-W6#VmS93C4RyMg?F zUNXsHprQHA`M(_aASg;5g|$`NbxyAdIHl)29y?w1;`Jf62{PCP>@uHUb%b(G;x3se z;WXOmM(_N?LW_(XboAICWJRRqWFpIr`>0iCC!9sTbN14VH9}a+wqdi%^I$5(?%MWF zN#tIIvrh3h@tS0(>zOd)EqA_S>8K64w7}?Ks(BOsZcMQ!X1vlOxrkGM86i%|#A?)p z(aoB4Az%~Ub|o%|QIu7#@sIIKJzB!SdNV;xHck!mg0Yz`vJms!H{yFnjSN?hES(|i z$Kep#{Svg1dlC;j5#sacex+BL2>X4-P0PEFv)-RMEsu;Jngs!(z`&jgN=VMbXQAVu z+kest-+*?1kYUA!{v0AF>ti`4Af}r~^+QM_mG+BzT&n1({;g?S<0n;pZb@YSUfCkH zQSm;eltZvlDKBBAfrGK1(ccen2vBt3vp6v(0II#??rs+ywWH3{E~L~V55?t?6fXq| zaMiA5rn}QJ3*hTY%TMrq!5;b3EXY^!_p0F=@2>pU1-+aFK@e)_TNy}OHLk;wNp9&Y zJ!+PpLWr1UjsKdfX^SA2O#7=tK$EUZEZB+^GautE+?%CjOx33B)$CD+Hs-2hdHERV z+01Noj-*oUREZ)ief$KvDM`5w+~sukS+GeZ{Q4Za`Bk~%FfSH(j?#~g{X-C-`HPt_ zz46#-kmi>nN$B4*F^hx2OVp+pIJ&{;RNIFAdPUB>_m3w&TPBP+xQ-U0>|*854MQni zghGhye{+y%CT9ljR)T~+(a<}CL!Z@-wXRw{N35YU<)NIHvuFpCe=jEt*kYgRvQV8P*=0Ak-q zk%Q8o!|l2WJ7@>DHF^~%D*Gq#NE%n(&7^*kM^SXW{Ne)mMV!kYZA}$X28BuIyDSaz zYp&lUHUlpp2P@YMdIuB;W(y9E?bQ?u8+lS)f)Y;V=WKd7$F3AFm{HLkaO=iCbVwqy z5qn?DR=76KO7ycgjZuBf&b{Z+2Lc$*`o}J$9Mw3Ex|AV*KVz;=GQjYymr7JGd>~sa za=|9zO3jWKI-VuaundM+V4eeV4!n>MWmZFT^XaMH za@(fz=_zGF)~@>*su@g8y~tjh6GUDhB;aa0fo_>jjUuZR=rwVX1;Cf-&$a=un3##y z?Yv^-Yu@o*ZcA=@*0}+kjP=IQ79>3>@;Bd$l`Yt4V-I@(qd-}Ctp2iSh<8=eTl0F{ zXio1cI;IcVI30shf7{+zFy2j=#z{2zGlfZmvtq<2|1mhd9Q$t*#@m4=^t_M&@PAK4A}APe1>Lc+sez-fH`*{|R`pD$?;(%N2mHsx; zDg5$d)PU9cH9tFa!igbQLXbz!&MupPsdR$;nfd z59@xGuo%R{CPhX*aQPN64uZA9Nc|7TXZDPOb^^%U&P$iq5?z9llk!Hpc6NpQ04e6J zAVkV{T4Ks7mF@0dODVwj(-a|g@_5{zhS#N%A0rmn#BbVjH{73`gfN63$yt5Sm zT>9?u(e=dG^KqZoGUtdU+wNZLamfnoYlxyM=bP$6#ljbKHj1~R%DI(_1qZ3J4jlC( zf1PGJyHDOX*z63L+=yP8*JX}Rrf5%$l2o@@DH^uykdrvpACLG*Z*}@&y~3b505B?; zKk)i?U}dCNan9JM!(N8(vdpEziLi8hFnJ})`sVRHIGAA|H~R~WUw_pdQt1?OQgrs7 zBjkI@#=5^_J2y$-8Y~tgkky6ChQY(anCt2i?BumWDPh&{4-lY|+{N2gaoPyu6ahCr zzl?mtCJ5~1xOSK@E~sj7#n|kR@V6t8pV!yg3(qFSx=wzVaqbN<8@Bis31Ax+^bvrj zr9tPi3e$VT+{0LAwV?~qJ)z2GLX_{k3U}X1mKETYkbYcQWRH4t8qfDtUikTTkDRsP zK^kRNzwKyX@kgfgEv8JDflw$jIIgWED0k)*!_!mlBYcl!8ln~1%L+bv<-wh1;4%4A z24?GFG$3>73e7f~1xPj?M$+#PnH>rU9_X%WQmepd{>o4}6T$EE^3!l)aK&+8ISeQg zC;@VK`Bi?uJ_zodxp=UoYxi>yQw*DS6K@f4EB{Zlyw@lHoxznTxP7ZICP2%7!$rHX zJHvPox*^+`;qcmN5hSj;Z?uo8%+Timy=!xwrrzs zFxt1zpXiNWQtz8g#I-zGHtI%M+*{Xs`!Lr(8^=3A3z7Z3*M>4*eXq9~Upwcex7yVx z&dH|F?4IvHPMR>!t?eZf{?@L49G`nJI|id?-6)mi6Arqretg1E-Zb|5;J$}P+?g*D zo`MB_v&(GHrCHNWrFzq!n=cU<@aHxke}J9S zi2i-alC5{gq9?qrUd8z4q zJ(}Iap{I7kM<8Ks-gaW{u<-;{Et7z~yLYm!ZEE}M;{h((i*qY-v~W}+N zE`wEJSU#=v#Vw4^Euyd82kh=4T>-gGkqeX~Q64DC}{sU zT)3*|A>7a=UDcMYAL-mxzXx<5Y}!9^wRCR z2it}OshK5 z^NogV#4rOu$m%wUC;Tre=N|s3gR=hj!p=a4a-qPUxqbPeYvO-Q3!>LPb#oTLwu^@r zQ2s7KeDwan1KjSC=kX)2C*!30wDW&Tp}_$o<3CM~nVLOYYnDN<|DU9P;;Qp`meSm? zWvCbPQI{z*Srn0H7@VIMJ7_-izB0uo3ZcdGH492s^j-1LHC=TKeE0t|Zx{2I?ug@x zOWE_aBfNzVmUi$RL$Z~?7*P3V?EK8eKOtPs8({?imejf@IZ;dx{%F;IHm-JDSH;gbkDtlw=L(WYjdyaBXmYvuPcnFt$&s%C0ai?`fdLO^rR%8=M!PODXu* zW;m|1d303a#xEZ^2aX3TrQ>dZ?x^^2k59vLQ$Dz??Zlyd zNz*=b6yh3u89}RD1ml@K`in2d^&PT#r=xLqZCGvli%s52)82TQ*n@|`@HG>1&^5E> zoa_)Q;oI9qaiy=Es6zF#DmLeLt-CjZ9Z=*`H~z5sIa0-<+2xbd`qAT_-dBSE?h$>I zz5i@w2uHqPsw#9+w)(NH2ZdC0L}NEdx+6V3JH&OWN&mY@4yt}*c!@joG%G{w7^F8E zTi4ejHgVz7=ofh<{l#Qm&2B>QC7PQ>-DIF6_h8+F#r(txxH1lsm7=_KmIR)#v4CGu58JsxPrugO{N$hC=l0 zkI1sta2&=dICS&8FRnySUNCb$1iYDo{yHJ~$l?mq%!@+(L%|RnIhJ$|qAUgv!>KQV z8Zp@T(~nG^z|=_2ymEQ0FyM!3Xfk1fekX0Ve4|`@pRl&g9Fb#r*@Tgk2gNwStQ zj-nD}9jBP%*1aBj+kR5^A*v$L7bO8oP)yWU+wD(V(bFTed9cV{W$Ks~ibOiTE2lIT zk_AF{){h2y5NqZni~K$t4rA#hoFQ;#;5ydDFwZ|u9i888a8)8)AE{?}^OW@JA)4i~ zwW>=>G<&>~BLZu8%bGtfux_q#Ka}po4gmo?c|7ks$Ou>N>~;zO>mT;nl;FRVUpVb% z8Kb=D(RE$W3EShXZTW;*B>`W7e<4EU+h9mvn9$upOc{aPnhNk9+=A_(N}p6r9h;OG>jd>CU%fRz^Iq};ueP_S z4TXxkIt-8r`{eZGCzp{WzE$K!5^lCV7vyzHvOPoia9N|Z!?nU|3T4=L9qXU$v2J{4 zqBRT97FsOzZ%e=92P()D$=60gzZB0J0(#v^*6Vk03H$9l@gPeF(;N|%yBQv3Q~{ZVwW$jraUaXHwn$hWKNlu<$4TEHx1T1i{#&u zjU^7mer?<{{waLQYic;J-g=@|4=ulYYNjFQQf5}Uh;1fIG%YliNxBrY#!IsLz)q>Z z#KSW3BBR%vKy@vRoKx76NzOOeGbz0kf+4u=df5)~m*;f+#pxM*n%Q9!=>v_-7*+VJwxla~2ANoq zXRq{I>b>WeGlg5|Q%`!#I>)T=k9&F8uCzL52E6qOPOUkYuq5yIVc66p#$Kf6Msbpd z#OdLId?2tI2&In#vMa|zMgm*1>uK7g?I0$ZnwU0@P|Ua*omlfDTT!otTv}RYSN+pC zD&(Wn^y0~LTUsOS7{i0F01i0XZ-6p1x6L~1bp9bh`djpk_|h4-0v8I&Qx=X>v~PCM z*irruFM)Q7lBCeu%I(f2MUrGd^uwFZ&I_%s+TzxO(l6x+nL0!JW+2i10e?lIt3sdA zUJBNlWA`7cv9Pkzqx!KVJEr7H!w<+`vN0uRyO;0}a2=9$4tW?w=6g~Wt=bq<~S(mtXRw{TDA)0QZnf6)lzELPf6Gp(dB#JMdB;{AH}}4?e;RkKLa$V4q=E-+<*uD7Rz$R$%-)CM;hxvA=@hB6!j>Y zgo1`@ooWW5A3(xxZnKQ$gM-0AlGgDr=42Aiw#X6=#4NxW0C&FqT~oE6B(s`{P-Sue zl~rYcED<6#KJ7VAR6GrOwqJZWqtg~@yZ%e-VBn2OlF6Fhl(T3Hb_^&<-^Ao}2kRzD zhu1xd`G}z=B63nw2C@MM=W3GK*_-e$jklchRVePwMdb;If#C!ncFgvdAGo?5Qbvbv zvJdVXC`WG|5355dMXe6KWi(KeiUFH5H826VyV#2o?qO4By{W+kbg5wQlERNcKbXr zBtb#(Sh$s9Zf8a2!la=tYQCUxAO~vP1QFBs5vg^^9ZlmqmOh{3Pr-G*lB*hXIpq45 zk_Y|TfA24;tzye6=b3a*S?D9ZT+9^2iwTs6_HiwsJ`XWp(S^sH{b&iKY5Jq$6U!;m0sArfZ5|V%iAn<258r2BJf$KWbcXIi=piQJ z*U!f~WNwM*gr1TY^LVMz@1zY9q-_T>$GW7&8YcZ*WeTll-ky;J*!9M3j#PeAD(UP@ za1v0&G332-bQQsftv?jW#huu zM^1?7MQ82^UTy5fsNfFE58Du<;#)Tbjy%tMwyOYx$`GQsnhIJ zu?-G)T?11unk@15Ttxj0c6~JUZ)EiP1$~59W(}1D?@G2f8o*{$z$gjYgfq{&mUVb! z=%(Fzg{{>e_g*c)_n&D?L=8q{50S;W$6u@*p7Oppwmqym@fdJ8Xsi+&?tfyIlsxKq8C)#J-`gY`u!MtdUYr(G3=GP;L>P}vPGYh~$1mn>mR9iB{ZkCj z<30iMEg0*WgXJM{K2FMzZuipTsPQ zO{X<7oquoRHTydquf~Edl?db;A)GRjVcYR~^mA3iPUO`kd2X@0eb%Vyd8HU@P zYbEu~RkAA3k}kbTN0qkMutD0eoch}yspc=V$05<#^7Ia)&fX@#a;wLpLMx&LAxP}J zY!>{igP{h&Vr)=T*u&@EDGZ5RUVAb$QCS);6-)fa&d5;^a%0Q6nq*Z1a$Ryt7^RGw z3>6?}O_1&_5RTU{e^QJ3tk^yLP9VODRto6Ln625$#TbsfS z)Et^_AC{^catcKit4nQmFJLeEYS-wHpG+k~a@=G=V<5>M>?B>h6*OD!Y`X7@ z1o{%^p`w=&+#Ac7<|&Y*Lyn#pZ6X7C(72wK+R$fS`)#4N^tA>1`R7lm*l+B>`fm;~ z`H#{N7i7m(3Dj~Ux(kH{1DD*++2dD!oSbQ+&X zVgKAXCIREgvL+^1rLgsKQAyHAlXN@N#YiZ;VQBj-hHJn4*yF2@@xu+Y?<@{Dq_-=M zWn$_n8Jmw7NQ*QUYBItLrC&MgaUjYPFuo<3$^cr9L`T-E9+pc)6f;sUE*#tm1gGHj z{Q@n#*tS;>T6#I+$lMV2rl*pcec2mfGIyJ>(%|G5_UGKsYm05q#{=Xe(Yjsy zM{PD^?5)-;%g#h@G#z1YgL2-#4GV9z*A)+aY)TdJxqX=TA~-Iy2)qPBaYuBtU$`)^ zRkXFpH5mVv30}E1x!+xomg}nMeO{xjkSY#QKtd3_&Onkcccqn&7c0UDExJT|b#HJW z8I5QZ(xml`$D3_f>{57d6Baj^<`*(?*yS-7FqTFrVtc0@b)qW$`g^>Sl1Yk-9wwJw zQD4o-Os<~)oO|){^g*OZ7Om&vgyff;DhRdJH z1ZUZE0GKF??d8y<9a-RGig%9!_qiG&+3JGt%=%2t(6-%(4Zd zkyd4>Ij59i_b81fV<(nUMC*~^5roh9(H*8GmI zJg-_{uS1uYl>&0hrtEJ`jb3L;iTHA8dV$X27aN&dRPeJ*dX(y5=9^B?s@>=CE1bX` zx_k@F`V~K}IS#x_J4j2O)X})aE9FVelLrhAR<7V0tC~SkRe$%yOwGl|#Lh27~qq-zAF{q%FY6&;}{cT_>)FNe#pL% z=Q+1{Q&VuAVWa*O-?sGk>o<{iGisE-bh}<93AQ3B1Qvp&%}6 zn^??^Pe7nm1|Cbkl*LXY#ZD%f&>dwFa>yf6^+#aohkGy~P?7aGlPylo7 zEMv)S_r>G%esVZ!+U_92G54rPwdY8TUu_m;Oq{=5uSF3Y7MDi_zsS>-G;^Ztw6nN()W$&22 zp!ywZghF)_@yCVXy_YVfcut zrMXOyJQhU#*N=&J?08bPY0^K&qb{k8Qv5iYwhO|BRx2A2PKk7I6d#%J_T)s)J*s8- z&Ox1Wa9vUVm7tX4x^}su$s63ZAoY03$NY;$9Z1d!@V?^vjES`iOD1!JhKM;4TdkJY z?Qd_+yvq}t<6F#xWp*ZpuJf2ueXA7nFMNMD30aTI&8iArS$3T!gkJ2$>E(R$Yix_& zv<7ac3};***pKa(97#8Qk|oA;(bpp-6|KF$iaT1z+cYPu&g6tFcMWz1nc|YyG2>8I z6H#eM3y4o#B&r>{#S|tfRP}OejzV}Y)P)4O}9?YPX=*MT##&Q!auNFRqAwVTcQaC@=t1@qJl4 zwSS?#*Q9J6>9wfDVhXKgBGs0L(T4Q?=e$L?^^PE*ofEobjwac}z@T-4T+Zz_KCG)S^|Nub29%NC;s5YyBz#t;^7tl`N;vIgd^!)9uuJh+J8?Yr zgLPuezDw;eT~X#kJt?iBEi98&YJ?EqDR0#Hf}~ADX-=pPX{l3hOgOGd*57z&^i8h? zwQw4qxn}&4OE&Huh(p6-D{85N!dn)Tw;g4qZzvBl7rSY`<`^IH+?L)jnBdddi?*GVC6Y=X?_q{$rIO~ z;OL9`h@F`3nz6=3@tNUA-2$x){H;+Zwc#O=Taldy0*awf9WF9J;Hu2D4G1oH+ z{sxzyTi=*r(_Im@Ap>rd4R>hEa*o}yYYEx_5i&Xa1KPT^{hTxB4r-Kjn>KxywRqY% z-OnALsV`5I_~iYw6ep0_t9gi@EQaA!sH0V(>_c zK6^L{fS`S;*eb!!8tqNnVzs^rz%R{Of;_8~#qk%N0;!`F7wG%)9D7~SevCIZYh2lI zNyj$TM;E8~Hj|gcIcZ(obDIh~kk!9nt7V4fVzTh;sMG26A7gL$lTno7J@TMTU!GI` z9pwDS_VYAN&Hhzv8nzO^NBsn+HC&oes~kh35SI)hr9tiuq2V^v>WHUoT=8(tQa zCwXofJhAmQRaPV~29^wdsrq2%*y~WQW38Rfb`)P?K<~fGE;4W*8yNXNa&8$jT=8vt zXaosy5aOO+eHfn8nzAKF*ATk?jMD#3BSW9sp-HRLEe~e;MDvY&25Iy6DtG*g`3?nP zg=UV&MW1;|@=i)+*-D2Mw9pZd+fVtP*dC`Y!;jbvu0?y$>IlBXf{BU4`tDwx?h8EE ze)q_I=AlF*F$r|nW|U&lJ-AR-e5Ef)RQaBgLZ2>(jd z2G;?ce`Sf!7O<)~+%6ZtT-fWT!8KE{y+EG?Ps!0U!BHW!{${y$%OpHt4zjO6l;zX% z)(r0XQqsl`BTnDl-T9(}mp_XT^ZVT6?~|0X|ATh~${Z#_l7ps@_OCh>oj|!yY3<5v zObma@_GYc!s@xjPcU`34w-50R8=Yz#0NP9=&T4a!grfcjrHD<>IkNK4ah2WLm)oSp z4mJDj8G$Tdg3dUXIG;U63}7?-P~3dmX(sj-gYZ`O(_@VUt+WH>4TWKQJ^p{zk8al# z?Rz)p=DE^ww?zEpw&@-poeH)9!IK>(1qvYoT!Z1hH!d+tQ!*9N)sSH3yv)&0*w_NY z4T0+_HkPfm>iW4BZWXF@64}=MGNTFVAUmi%&M~skLs}ay0iJ`t2p9zToi@yAJ-cx4;YYadCLbP0AJQymaaY z`hrRARE*MVVK>+Y+|`>BU?2Hc2loa(hZv2^Cznzi{-Fuc8;%S5d)5e5*`*k+XLKlDUF7-G^6*u zjz0RuRjz}7U0R9ne@C#}iyZ|x@9Z4KuovjQoDl?S*1?tOptWNp>}F|izaZPV0}mXk zPy^4|rT=-Fm%7AQ*?YcR*ML)e&xLmQ>(FUv_*~ZfZVqFNbvC_O;?2clQj3p!szj0^ z1Qh3~o&5F6TF>i#7a7ak(g+bUuPKTsiw5iJ2G^Qo8v`IZWgHMU{Kg5eOrNB2CNqic z>)&Mp9<9rNp0gdbWJB>%VxIpdo$*#!@G2IXvb4jSMlBqhH0Fg~*jf-r_9Cf>DZpBX zfxMbh0j%s2dmnngGH4q0@Utd$#04~qGYQQCx|%C%kp*nHI3O)(gc>4IR!%!$1V*y^ zjqq%4?5t^RYI4vT@wZ3G(!{|>KQwl>;Ki`ow3<+{cOjd3 zq)}}AmL%5Vb}_1JBUcbmNt%A-B#22apylCgJ$z}V8IO4(*XnfrYT!1tMj|Ce9(O8g zEqO&R{MlX(#+d-E4_=jjPdUrey^8eN}#$Sd*yUNJ_IYj^OX)eu* zXNuvPO^m2tXw}ZfQLwRFQlV|+Q~BUn1d}VDuAcvQ5GavE%`Y{ul$O{oUeH;tsPb(5 z1JsI4xZrh<^3>4JgBG|Bk>VXjvFU?FXl0a~Y>83p^k?@EQG2QGwrjMJB&v%lC#4fb>B9(1 zc#&cWOb4rAp5h13597OLd8!9b1G||=4aGt!fwhWh`(>&JIu9E~K>+p;HpOKZ>EgxI z$fKTDQ#0l+-! zb2-;m6YQ74aNozzn^WJ@FqMN{1e5c&#X|j7EJsJg)G`~Ld|8virY>Ka5rygdsjI7E z*w~j{5cqE?!mPr>zoI0=d(X1>A%Kx@nwWC2x{iVzi{KyLG*O=JV67F4m30_(UTGNJ zNb`c;i{V|*A;IU;O4g-#;hVod1%a|JpHN&ss^^x)Tx%ciP==)=GAgyPNk!)qGL^(#YUD%jOS#Yji#liu4> z%bU#K(Us`~dE2Bj0YyZ09|DC(oZL0(nfhA|;--xK_hCgT2U6dQ1BY$Zd{qLECY=s^ zY{11@#kYfHSJgl`zoZ<83=|481pYS3?5uilz~$%WGZpON>yyRMez+||uWzmB;)Z!R z6oa+OX;soiV)(FO2Vqv7x?evgXR;GOcPVmuxCbH3i9CNRWJoFaRN?Vi$NhxJ&BD@j zIvbu7i{DP&f1$-lP6*B@T{hq`CLZv5RU%)FEjXlLZn9KSApkw?@Wufg1^&juUqSO1 zKOkWT(@cB?>OPLxUnd9?6ITYeTpM#Wdt5%gm`F?>n$kzEr_K?<+hEdfI@cB_NU`?B zY$))cM2lRsiG%a-W43FJj86Fu4_-$3AJFB*Qju?N-O3!5PiA%vWcx2m2?kA)w9lo` z*-rGrhuPE4Ok*3T%+B~9Fe7J>ywg`K$7`-Q4)Wpqo{C5oG`Cgu^^u9WiG&fw)SOS4 z;b>@_DU;V^&$rB0PqI+69WS2+c$lJs8>11EJ$xhOu#tU27>J$Y;Imion|*Ebtztbw ze)odK4fb!0^d7X^-}C~QAoWL@Tl=cpx1lilRlnGv0>w&M8FF+>oq}$w@!B#Z<DZ zRff}W`w%b=eGYvyxvhVyy@@ShK7e9Fgg9mxsM9I|3MRuzbCm8IW`Nhj=^oWR!T|) zgSxc5t4NZW^iz{?^v_m;V&mTa7LurCvJir;dFuD^FCxhpdU&9#y@?*yLH^X~J!EsC z1hMFjjhm(H?{0RWy$IF;o@93RB7Iec@hdEh>jEM}rdxbx z41LS^4K&V$I95WXF_Ms=&acolR_~ZBCQo!s*So8Et%Y_RFXtBt-d9s&m3E_bDOo1FP)f(y$Rp?6-KO5Mh3~cUwFQv}a4NRDu4c(9 zEK~@@FP*NRj8+ue%w$@%eJH=FPUWIB&taPIHGdRJC|t-n>*l{{mn?8Asz+zF9sw!M zS3kCTF#?Vw0REyF9idT@m^BC6nAVolbyKH@cwz3VA|!fmrYO=)GVQi5vZm zcbj({Jd6qQ(s%3rxtUZnnhb zAl}1r13EJqD<;SVFs512Q8#l4N-M2$jn5fJY{ptp2Apu#K8x5Sx;1pWT53DaS$;tGwuL z8BixovQ3{nfUeaGr163v*vJUp7v;x&%YC~zDg_i&l!(o(^EsZ0$;^2B%|rI2@ark> zC6}n{6khKbCBl*+&t1`WWbY1ojohS>i=L~Vb+DN~ZU56R+%VsK3p@{YIw0N&wx2eb zgCiWN%l)1mIf71lM{q2JgY<4l$tZtsKg5NOQ(-%W`8~Aa1V;+qe3)&rvV!(R`d-JfJlZR}#b|4c&F&<@H<+MzzXBW0y>5fEg zm)m4p|JJ=@1vQ-#%-L)gIBCWINBEIzsJowzt>+=-8;QV~$q+IqqdfNoou%V@WXi;IW;kN+bE?(=J ztvtnneyIb;o)7$hO#~>4X4jZ9Xfhs`-j58D^37~;JfblA5|lDAoqNZ!`70^Z2L^y# z8}(x%CPV|iG6k-!%pi*HyK}MEK*?e}<@SZe+XG`ehf?>=7lE9WfQXB$###*Ma)GvF zJLRDwL%Y+`(X3_g3Dygn%^GfVjvZb&F>g-4WT3?loz<1cn$Pb4v6@evx^;M4I-NoG zm1Z~~25M-fDEV82Uol>BnsTDj5t~Cf)P5mVew~tf5Gj=CvWVVZ73X35*0d=e$TrOJ0B`B|G$!lR9|89b zD%NMY{g)rp@qgwxigf5R%NApNVsz>qqdtII)r2a`#OK~_CvDTffT+Py_EU+|IOgJG z9%PZGHH`Pkm8tw}oCS5l&Cx%EN{@DpQ^O>BkADzF?AN>~!T6xgjDgp4rE6@P6+?UG}jdm)_* z>3&crM(cWea`Rp-l_HFhQ}erYc+pGR59<96jU0G$whKOP_d$3)I*Wo9S==%74#(cc ze{}cXT>ncJ6iSi)e?dl}r(MUs)uKa5)1mmmB^ScgOr-mdS>dw_<}}6|Y6_m(kh9fh z>B*Pdhx>5Y_A3P@-b_~yd%dXt;G}dk>99WDfL{Ik;NR7DE6$%}Lk*SGghJZ{MeR9> zB9ym~!3uxV?2a!_sGyK(h*oE;+apfgTIHA+Wk!G<={^mU80dEeD(6+8+Q~x4B{#iA zX5X>)@#MmocKcTh+TFm*l*STdw+~D#tIZTc-Dl(Rs9I2JqWWGa(F*!Ol9kvV z7JC;d!8R@W!it#D$s=i1F8Bx6!1g6N4bel%#j4FmWM_{goseA{Is09Csp!@_*w_C$ z2C)~q$WypzSJqmr_-cQv*4%#DBux!BQ(etW*D!q4`IHX5zA$~4z_+DAHRN9e&kH|f zliIjU`c~c%oA2MkY!y~KiQFz^XHCA_W$EaivX3l~cqN?A(I>y=*rcz(cHF~#UhMP{ z>|E{mI08JMJGwoI4=7&27rUi4WwY`65i{jrJTM;b`6_>TElpwmK7rCGCG5kOV-!&r2vF2p+8dr4c0nwA(SzVZ-Hh|`GjZFs|{s{Z8+G>uCD7Yu?h$1 z|HijRLYbVIZQ?9HSU9(A{a>EK^>MgpmF&#`r{Oo0OZ%sceXykcQdCDsb4D@`jR=P< z8fvy%yX@7P+KznXq>~O|+b}iR7eQy*3I=wig8H;e=L;Rc_a9G|Twn=b><4$e4i_8D zq>tHI{ILl8KjA-lscojOk{gRgDtY-KpUHcZ$1^Zi_*&xMQ7*uC%1PDl7F0AY0wTzF zS6C12gr0W{Z+OU(Pg}N|JsGi6Z|JkUhnJnK3Za-!*ndI9+RuDNoyk%aE>IXc2e1}b zJ*%?8uNji{@X;sf?c=p*h25ODduk^|AxOP=`H8WosD!ngYRvqnc!2}D^E>Z{^d{o7 z4h(Q-wREC!>_dCJWB# z1)Z-*7u$VZykI)O-b6n`gH8%-e>`wO#qjQ=RXtcsAZ=z5_|WmO;J7%&pf+ za0pP*>@0L=IM{o2+ZH-4-erz)c6sypK1%{6`0TfC)~y{24ccARH@(9|JYao9)Bv){Uzk8oA{)jrf%;Ql0M_BgEy z91|*2e);oY^}u6E_Tnu+)(XR*h|YKJ!1W&>ru08KV|y^b5!hNrx!Fj}C!yBl6raM; zdZia}-=Tf$y%;3kmiQv4_w6fapzq$g;Qd^*>I&_OB;$2Fb)v<~|CDLN5>4v~=glz& zMz0{psJgMpj+X^6wG_J0)Ld>D~+r0+8SX#<#H7mJgc z_`UPT)0a_%D(wMc{CcRt^enkZ%Ap!av=$ z$Q?VU$Nv=Fh87xe4vZWWWY`gf7(=d#rk>w;(TJenwByZxCob99Y*UmzZ4llnE`>r-Qj3qQzz>_lU-G?YthNNoheb#lD}NTCN6#3cZ2k_ zcCTLT49k4+o7X2#nb&OAi8y+o)EqbG$QGNSV8h(oS18T5_#c{2;IZSAHC+?J)4(&t zs9E$aShzxGLWHFKw`zbVHnOofUHExs;@Pt`>(j(?=+ZrTkJB&1e@b*OecKQp4M7gb zI;XZn*I`dZN!;s{k<g9}j>weDuZb8H2 z^_G5H7rJfYITT>~sb~3r18kP?VMc#hJ7uQw?dpf#zItZ4ul_g2duWWTq?n_ZVvc=3 zh(vmZ6{S2}J-%$Q-|cM;0{Z-cj~|ilDJ*Zj@0h$q;Iwz2EPxJ;Pow9M#cDn92=!F6 z!!OrM=zS`S60Mfi0XwhLq35pN>3UnT_a!7an!$)yBUsuw!=;v6V5|V`zTSR zEO1lSd9#08$L`hK=Hzi%tgaqhOaPYDPogkvzlMjdS-y)gI**;k1~Ps{LFfWsyh`?J zfYTBHM%w$e2`(2kN|<~8(ba>>d3^aj-LwMzhc+k=e+s#r?XND^fXR4+h#M~yQqlZm z*bG3Z{T*g>h3H{X{C_lZ?e9#l0lY#n<(MeL%H=edB|O>in29F26@|^*$|X9RTtclf zXG!TYtK2G-iJEfhkle2)98@g1Z5e7#bk#DG4im+nvZjH$OYA}aL4{o)6NCCSzs3lFdu+a-S)iR*!@rar5%>&c zPJGk?BuD(H{sQD-W0FYlhBXCPpW4`j1z~hxh^t__(=oc?=M3+_7Qpcbu zP>f5PL~%g3#>@ANZv@kS?p-V(PqBjb_X_4??TmC8WxgmdJwR=!39;SB5su*sC~#y% zmE8jFyU@ck$IKJyO#3t@HcY8`AYEr|SLt`Y+9DzQ={MHCoJ3vOWX(e-!^@w%O(Vcg zDJYd*KNvoxp-L_~#5t`Ubr0mxf@WBkgAUHQI9$)51z3j7LI<~pM)$u57AL$ZKbsKO z&I^{g)YgQs6jL=XqtXxON=)t55PvyAX>^)v$4f|`*~yjj^%vX~d+efdRwLV_l<1 z>YO(>TUf2pi38IZYzL8kg_+*l>U?@j{+{9dJ#$HRW(sGzpP(2v5@2UHY@Zz^2cfpDXUdn8zPD8b~|#2MY1tarILYqfNBrY@5D`@K2Ux zaR%x74i1#vTG}DXGfrtM&Q*E{b=aeuq-dZKv&2SKx3ZL0n)_rJ=#0_&i{u_1Rjf>- zMRHQ^B;Z{*mV`6KL9c7Ex79rwEy+zyPiya#Y=#Hh+!4Mph!xCT zqa&a2eU&CR^2YZBtgf0dnIphUe|iHR%v*C9Ta~B%^6*N>sW+`JPp6r}W=*H_n&%Y^ zMPAFrty&A5sZh~@#CRI+=qg&@Eh74+RZl@s8X$b8IyHAOgIaid4zHD{#0eqiiIm|L z@`fmGWZLcWwt?zI*tY_ZeU!O3%#fN12aC(1Q^xd?q_HMoTEdISHB<=1*pMVt29>2q z&dXU2t39MbOlA)#*ehF`$aM!?Ql~emj+H#!`{qU&IpT z5?2vp9KV~BiU>~dFt&Mm2yUcvWFiZ=mk9)iJQGJJ{P$3=V`D@&?D@>_g~WWyRIlu> zS09tI5|X+=Oz*0rPOQi3!kqBoEE!5tK3G$eN(q=4dO5aD>y8o3_J+hdI}o_E@x}h} zeuG{F!0xu8J9ypC$SD2b(;yhRd$+Pexn_Vk&Wq)WH-!e`cR2%Rf11Y{t>6b|p70^c z-);hc8=dl?LAJBymF=YGv7=XX{Sdpi&uvjGf7Wj((l)GHP`xr!wz9Npfm_R)dF6t^aBs!;$L+3&0n&eP z_i{tE{mUtG7bnL{7V74ST-Rb(7DeIo5wy=-<*Wg;q%BLr%+mnLa;#+a`19Ao3N;Kf zVIr#tC9)>8IPq$e0P1d|8( + +license: Apache-2.0 +license-file: LICENSE +author: Lei Zhu +maintainer: julytreee@gmail.com +category: Development +build-type: Simple +extra-source-files: + LICENSE + test/testdata/*.hs + +library + exposed-modules: Ide.Plugin.ExplicitFixity + + hs-source-dirs: src + build-depends: + , aeson + , base >=4.12 && <5 + , containers + , extra + , ghc + , ghcide ^>= 1.7 + , ghc-boot-th + , ghc-exactprint + , hls-plugin-api ^>= 1.4 + , lens + , lsp >=1.2.0.1 + , mtl + , text + , transformers + , unordered-containers + + ghc-options: + -Wall + -Wno-name-shadowing + -Wno-unticked-promoted-constructors + default-language: Haskell2010 + default-extensions: DataKinds + +test-suite tests + type: exitcode-stdio-1.0 + default-language: Haskell2010 + hs-source-dirs: test + main-is: Main.hs + ghc-options: -threaded -rtsopts -with-rtsopts=-N + build-depends: + , base + , filepath + , hls-explicit-fixity-plugin + , hls-test-utils ^>=1.3 + , lens + , lsp + , lsp-test + , text diff --git a/plugins/hls-explicit-fixity-plugin/src/Ide/Plugin/ExplicitFixity.hs b/plugins/hls-explicit-fixity-plugin/src/Ide/Plugin/ExplicitFixity.hs new file mode 100644 index 0000000000..32cf1632a2 --- /dev/null +++ b/plugins/hls-explicit-fixity-plugin/src/Ide/Plugin/ExplicitFixity.hs @@ -0,0 +1,90 @@ +{-# LANGUAGE OverloadedStrings #-} +{-# OPTIONS_GHC -Wall #-} +{-# OPTIONS_GHC -Wno-deprecations #-} +{-# OPTIONS_GHC -Wno-unticked-promoted-constructors #-} +{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-} +{-# HLINT ignore "Functor law" #-} + +module Ide.Plugin.ExplicitFixity where + +import Control.Monad (forM) +import Control.Monad.IO.Class (liftIO) +import Data.List.Extra (nubOn) +import qualified Data.Map as M +import Data.Maybe +import qualified Data.Text as T +import Development.IDE hiding (pluginHandlers) +import Development.IDE.GHC.Compat +import Development.IDE.Spans.AtPoint (pointCommand) +import Ide.PluginUtils (getNormalizedFilePath, + handleMaybeM, pluginResponse) +import Ide.Types hiding (pluginId) +import Language.LSP.Types + +pluginId :: PluginId +pluginId = "explicitFixity" + +descriptor :: PluginDescriptor IdeState +descriptor = (defaultPluginDescriptor pluginId) + { pluginHandlers = mkPluginHandler STextDocumentHover hover + } + +hover :: PluginMethodHandler IdeState TextDocumentHover +hover state plId (HoverParams (TextDocumentIdentifier uri) pos _) = pluginResponse $ do + nfp <- getNormalizedFilePath plId uri + har <- handleMaybeM "Unable to get HieResult" + $ liftIO + $ runAction "ExplicitFixity.HieResult" state + $ use GetHieAst nfp + + -- Names at the position + let names = case har of + HAR _ asts _ _ _ -> concat + $ pointCommand asts pos + $ \ast -> mapMaybe (either (const Nothing) Just) $ M.keys $ getNodeIds ast + + -- Get fixity from HscEnv for local defined operator will crash the plugin, + -- we first try to use ModIface to get fixities, and then use + -- HscEnv if ModIface doesn't available. + fixities <- getFixityFromModIface nfp names + + if isJust fixities then pure fixities else getFixityFromEnv nfp names + where + -- | For local definition + getFixityFromModIface nfp names = do + hi <- handleMaybeM "Unable to get ModIface" + $ liftIO + $ runAction "ExplicitFixity.GetModIface" state + $ use GetModIface nfp + let iface = hirModIface hi + fixities = filter (\(_, fixity) -> fixity /= defaultFixity) + $ map (\name -> (name, mi_fix iface (occName name))) names + -- We don't have much fixities on one position, + -- so `nubOn` is acceptable. + pure $ toHover $ nubOn snd fixities + + -- | Get fixity from HscEnv + getFixityFromEnv nfp names = do + env <- fmap hscEnv + $ handleMaybeM "Unable to get GhcSession" + $ liftIO + $ runAction "ExplicitFixity.GhcSession" state + $ use GhcSession nfp + fixities <- liftIO + $ fmap (filter ((/= defaultFixity) . snd) . mapMaybe cond) + $ forM names $ \name -> + (\(_, fixity) -> (name, fixity)) <$> runTcInteractive env (lookupFixityRn name) + pure $ toHover $ nubOn snd fixities + + where + cond (_, Nothing) = Nothing + cond (name, Just f) = Just (name, f) + + toHover [] = Nothing + toHover fixities = + let contents = T.intercalate "\n\n" $ fixityText <$> fixities + contents' = "\n" <> sectionSeparator <> contents + in Just $ Hover (HoverContents $ unmarkedUpContent contents') Nothing + + fixityText (name, Fixity _ precedence direction) = + printOutputable direction <> " " <> printOutputable precedence <> " `" <> printOutputable name <> "`" diff --git a/plugins/hls-explicit-fixity-plugin/test/Main.hs b/plugins/hls-explicit-fixity-plugin/test/Main.hs new file mode 100644 index 0000000000..df2aea3787 --- /dev/null +++ b/plugins/hls-explicit-fixity-plugin/test/Main.hs @@ -0,0 +1,61 @@ +{-# LANGUAGE OverloadedStrings #-} + +module Main where + +import Test.Hls +import Ide.Plugin.ExplicitFixity (descriptor) +import qualified Data.Text as T +import System.FilePath + +plugin :: PluginDescriptor IdeState +plugin = descriptor + +main :: IO () +main = defaultTestRunner tests + +tests :: TestTree +tests = testGroup "Explicit fixity" + [ hoverTest "(++)" (Position 5 7) "infixr 5 `++`" + , hoverTest "($)" (Position 6 7) "infixr 0 `$`" + , hoverTest "(.)" (Position 7 7) "infixr 9 `.`" + , hoverTest "(+)" (Position 8 7) "infixl 6 `+`" + , hoverTest "(-)" (Position 9 8) "infixl 6 `-`" + , hoverTest "(<>)" (Position 10 7) "infixr 6 `<>`" + , hoverTest "(>>=)" (Position 11 7) "infixl 1 `>>=`" + , hoverTest "(>=>)" (Position 12 7) "infixr 1 `>=>`" + , hoverTest "elem" (Position 13 7) "infix 4 `elem`" + , hoverTest "on" (Position 14 7) "infixl 0 `on`" + , hoverTest "(||)" (Position 15 8) "infixr 2 `||`" + , hoverTest "mod" (Position 16 8) "infixl 7 `mod`" + , hoverTest "(**)" (Position 17 8) "infixr 8 `**`" + , hoverTest "(^)" (Position 18 8) "infixr 8 `^`" + , hoverTest "(<$)" (Position 19 8) "infixl 4 `<$`" + , hoverTest "seq" (Position 20 9) "infixr 0 `seq`" + , hoverTest "(<|>)" (Position 21 8) "infixl 3 `<|>`" + , hoverTest "fixity define" (Position 23 11) "infixr 7 `>>:`" + , hoverTest "record" (Position 28 10) "infix 9 `>>::`" + , hoverTest "wildcards" (Position 30 5) "infixr 7 `>>:` \n \ninfix 9 `>>::`" + , hoverTest "function" (Position 32 11) "infixl 1 `f`" + , hoverTest "signature" (Position 35 2) "infixr 9 `>>>:`" + , hoverTest "operator" (Position 36 2) "infixr 9 `>>>:`" + , hoverTest "escape" (Position 39 2) "infixl 3 `~\\:`" + ] + +hoverTest :: TestName -> Position -> T.Text -> TestTree +hoverTest title pos expected = testCase title $ runSessionWithServer plugin testDataDir $ do + doc <- openDoc "Hover.hs" "haskell" + waitForKickDone + h <- getHover doc pos + let expected' = "\n" <> sectionSeparator <> expected + case h of + Nothing -> liftIO $ assertFailure "No hover" + Just (Hover contents _) -> case contents of + HoverContentsMS _ -> liftIO $ assertFailure "Unexpected content type" + HoverContents (MarkupContent mk txt) -> + liftIO + $ assertBool ("Failed to find " <> T.unpack expected <> " in hover message") + $ expected `T.isInfixOf` txt + closeDoc doc + +testDataDir :: FilePath +testDataDir = "test" "testdata" diff --git a/plugins/hls-explicit-fixity-plugin/test/testdata/Hover.hs b/plugins/hls-explicit-fixity-plugin/test/testdata/Hover.hs new file mode 100644 index 0000000000..8cfa245f71 --- /dev/null +++ b/plugins/hls-explicit-fixity-plugin/test/testdata/Hover.hs @@ -0,0 +1,40 @@ +{-# LANGUAGE RecordWildCards #-} +module A where +import Control.Monad +import Data.Function (on) +import Control.Applicative ((<|>)) +f1 = (++) +f2 = ($) +f3 = (.) +f4 = (+) +f5 = 1 - 2 +f6 = (<>) +f7 = (>>=) +f8 = (>=>) +f9 = elem +f10 = on +f11 = (||) +f12 = mod +f13 = (**) +f14 = (^) +f15 = (<$) +f16 = seq +f17 = (<|>) + +infixr 7 >>: +infix 9 >>:: +data F = G + { (>>:) :: Int -> Int -> Int + , c :: Int + , (>>::) :: Char + } +f G{..} = undefined + +infixl 1 `f` + +infixr 9 >>>: +(>>>:) :: Int -> Int +(>>>:) x = 3 + +infixl 3 ~\: +(~\:) x y = 3 diff --git a/stack-lts16.yaml b/stack-lts16.yaml index 6d4189fb68..1c527f18bc 100644 --- a/stack-lts16.yaml +++ b/stack-lts16.yaml @@ -31,6 +31,7 @@ packages: - ./plugins/hls-selection-range-plugin - ./plugins/hls-change-type-signature-plugin - ./plugins/hls-gadt-plugin + - ./plugins/hls-explicit-fixity-plugin ghc-options: "$everything": -haddock diff --git a/stack-lts19.yaml b/stack-lts19.yaml index c877f9306c..093583e9c7 100644 --- a/stack-lts19.yaml +++ b/stack-lts19.yaml @@ -31,6 +31,7 @@ packages: - ./plugins/hls-selection-range-plugin - ./plugins/hls-change-type-signature-plugin - ./plugins/hls-gadt-plugin + - ./plugins/hls-explicit-fixity-plugin ghc-options: "$everything": -haddock diff --git a/stack.yaml b/stack.yaml index b5b6d21530..927dd7065f 100644 --- a/stack.yaml +++ b/stack.yaml @@ -32,6 +32,7 @@ packages: - ./plugins/hls-selection-range-plugin - ./plugins/hls-change-type-signature-plugin - ./plugins/hls-gadt-plugin +- ./plugins/hls-explicit-fixity-plugin extra-deps: - Chart-1.9.3@sha256:640a38463318b070d80a049577e4f0b3322df98290abb7afcf0cb74a4ad5b512,2948 From 922b2a0dbeb889f55bac62ec0ee66f67b5604c03 Mon Sep 17 00:00:00 2001 From: Lei Zhu Date: Mon, 6 Jun 2022 14:55:53 +0800 Subject: [PATCH 02/11] Update dependencies --- .../hls-explicit-fixity-plugin.cabal | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/plugins/hls-explicit-fixity-plugin/hls-explicit-fixity-plugin.cabal b/plugins/hls-explicit-fixity-plugin/hls-explicit-fixity-plugin.cabal index 4b0dabe2e5..c2bd9c5262 100644 --- a/plugins/hls-explicit-fixity-plugin/hls-explicit-fixity-plugin.cabal +++ b/plugins/hls-explicit-fixity-plugin/hls-explicit-fixity-plugin.cabal @@ -20,21 +20,14 @@ library hs-source-dirs: src build-depends: - , aeson - , base >=4.12 && <5 + base >=4.12 && <5 , containers , extra , ghc , ghcide ^>= 1.7 - , ghc-boot-th - , ghc-exactprint , hls-plugin-api ^>= 1.4 - , lens , lsp >=1.2.0.1 - , mtl , text - , transformers - , unordered-containers ghc-options: -Wall @@ -54,7 +47,4 @@ test-suite tests , filepath , hls-explicit-fixity-plugin , hls-test-utils ^>=1.3 - , lens - , lsp - , lsp-test , text From 7420ac44427c201aa17ad8a023de705612304217 Mon Sep 17 00:00:00 2001 From: Lei Zhu Date: Tue, 9 Aug 2022 17:03:18 +0800 Subject: [PATCH 03/11] Refactor: Prevent block on startup --- exe/Plugins.hs | 2 +- .../hls-explicit-fixity-plugin.cabal | 10 +- .../src/Ide/Plugin/ExplicitFixity.hs | 200 ++++++++++++------ .../hls-explicit-fixity-plugin/test/Main.hs | 17 +- .../test/testdata/Hover.hs | 2 +- .../test/testdata/HoverImport.hs | 5 + .../test/testdata/hie.yaml | 3 + 7 files changed, 165 insertions(+), 74 deletions(-) create mode 100644 plugins/hls-explicit-fixity-plugin/test/testdata/HoverImport.hs create mode 100644 plugins/hls-explicit-fixity-plugin/test/testdata/hie.yaml diff --git a/exe/Plugins.hs b/exe/Plugins.hs index 656e8f7783..04c8ccb0e0 100644 --- a/exe/Plugins.hs +++ b/exe/Plugins.hs @@ -207,7 +207,7 @@ idePlugins recorder includeExamples = pluginDescToIdePlugins allPlugins -- (which restart the Shake build) run after everything else GhcIde.descriptors pluginRecorder #if explicitFixity - ++ [ExplicitFixity.descriptor] + ++ [ExplicitFixity.descriptor pluginRecorder] #endif examplePlugins = [Example.descriptor pluginRecorder "eg" diff --git a/plugins/hls-explicit-fixity-plugin/hls-explicit-fixity-plugin.cabal b/plugins/hls-explicit-fixity-plugin/hls-explicit-fixity-plugin.cabal index c2bd9c5262..087c1466b6 100644 --- a/plugins/hls-explicit-fixity-plugin/hls-explicit-fixity-plugin.cabal +++ b/plugins/hls-explicit-fixity-plugin/hls-explicit-fixity-plugin.cabal @@ -1,7 +1,7 @@ cabal-version: 2.4 name: hls-explicit-fixity-plugin version: 1.0.0.0 -synopsis: Show fixity explicitly +synopsis: Show fixity explicitly while hovering description: Please see the README on GitHub at @@ -22,11 +22,13 @@ library build-depends: base >=4.12 && <5 , containers + , deepseq , extra , ghc - , ghcide ^>= 1.7 - , hls-plugin-api ^>= 1.4 - , lsp >=1.2.0.1 + , ghcide ^>=1.7 + , hashable + , hls-plugin-api ^>=1.4 + , lsp >=1.2.0.1 , text ghc-options: diff --git a/plugins/hls-explicit-fixity-plugin/src/Ide/Plugin/ExplicitFixity.hs b/plugins/hls-explicit-fixity-plugin/src/Ide/Plugin/ExplicitFixity.hs index 32cf1632a2..b6f6c3fc0a 100644 --- a/plugins/hls-explicit-fixity-plugin/src/Ide/Plugin/ExplicitFixity.hs +++ b/plugins/hls-explicit-fixity-plugin/src/Ide/Plugin/ExplicitFixity.hs @@ -1,85 +1,58 @@ +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} -{-# OPTIONS_GHC -Wall #-} +{-# LANGUAGE TypeFamilies #-} {-# OPTIONS_GHC -Wno-deprecations #-} -{-# OPTIONS_GHC -Wno-unticked-promoted-constructors #-} -{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-} -{-# HLINT ignore "Functor law" #-} -module Ide.Plugin.ExplicitFixity where +module Ide.Plugin.ExplicitFixity(descriptor) where -import Control.Monad (forM) -import Control.Monad.IO.Class (liftIO) -import Data.List.Extra (nubOn) -import qualified Data.Map as M +import Control.DeepSeq +import Control.Monad (forM) +import Control.Monad.IO.Class (MonadIO, liftIO) +import Data.Coerce (coerce) +import Data.Either.Extra +import Data.Hashable +import Data.List.Extra (nubOn) +import qualified Data.Map as M import Data.Maybe -import qualified Data.Text as T -import Development.IDE hiding (pluginHandlers) +import Data.Monoid +import qualified Data.Text as T +import Development.IDE hiding (pluginHandlers, + pluginRules) +import Development.IDE.Core.PositionMapping (idDelta) +import Development.IDE.Core.Shake (addPersistentRule) +import qualified Development.IDE.Core.Shake as Shake import Development.IDE.GHC.Compat -import Development.IDE.Spans.AtPoint (pointCommand) -import Ide.PluginUtils (getNormalizedFilePath, - handleMaybeM, pluginResponse) -import Ide.Types hiding (pluginId) +import Development.IDE.GHC.Compat.Util (FastString) +import qualified Development.IDE.GHC.Compat.Util as Util +import GHC.Generics (Generic) +import GHC.Utils.Error (emptyMessages) +import Ide.PluginUtils (getNormalizedFilePath, + handleMaybeM, + pluginResponse) +import Ide.Types hiding (pluginId) import Language.LSP.Types pluginId :: PluginId pluginId = "explicitFixity" -descriptor :: PluginDescriptor IdeState -descriptor = (defaultPluginDescriptor pluginId) - { pluginHandlers = mkPluginHandler STextDocumentHover hover +descriptor :: Recorder (WithPriority Log) -> PluginDescriptor IdeState +descriptor recorder = (defaultPluginDescriptor pluginId) + { pluginRules = fixityRule recorder + , pluginHandlers = mkPluginHandler STextDocumentHover hover } hover :: PluginMethodHandler IdeState TextDocumentHover hover state plId (HoverParams (TextDocumentIdentifier uri) pos _) = pluginResponse $ do nfp <- getNormalizedFilePath plId uri - har <- handleMaybeM "Unable to get HieResult" + fixityTrees <- handleMaybeM "ExplicitFixity: Unable to get fixity" $ liftIO - $ runAction "ExplicitFixity.HieResult" state - $ use GetHieAst nfp - - -- Names at the position - let names = case har of - HAR _ asts _ _ _ -> concat - $ pointCommand asts pos - $ \ast -> mapMaybe (either (const Nothing) Just) $ M.keys $ getNodeIds ast - - -- Get fixity from HscEnv for local defined operator will crash the plugin, - -- we first try to use ModIface to get fixities, and then use - -- HscEnv if ModIface doesn't available. - fixities <- getFixityFromModIface nfp names - - if isJust fixities then pure fixities else getFixityFromEnv nfp names + $ runAction "ExplicitFixity.GetFixity" state + $ use GetFixity nfp + -- We don't have much fixities on one position, so `nubOn` is acceptable. + pure $ toHover $ nubOn snd $ concat $ findInTree fixityTrees pos fNodeFixty where - -- | For local definition - getFixityFromModIface nfp names = do - hi <- handleMaybeM "Unable to get ModIface" - $ liftIO - $ runAction "ExplicitFixity.GetModIface" state - $ use GetModIface nfp - let iface = hirModIface hi - fixities = filter (\(_, fixity) -> fixity /= defaultFixity) - $ map (\name -> (name, mi_fix iface (occName name))) names - -- We don't have much fixities on one position, - -- so `nubOn` is acceptable. - pure $ toHover $ nubOn snd fixities - - -- | Get fixity from HscEnv - getFixityFromEnv nfp names = do - env <- fmap hscEnv - $ handleMaybeM "Unable to get GhcSession" - $ liftIO - $ runAction "ExplicitFixity.GhcSession" state - $ use GhcSession nfp - fixities <- liftIO - $ fmap (filter ((/= defaultFixity) . snd) . mapMaybe cond) - $ forM names $ \name -> - (\(_, fixity) -> (name, fixity)) <$> runTcInteractive env (lookupFixityRn name) - pure $ toHover $ nubOn snd fixities - - where - cond (_, Nothing) = Nothing - cond (name, Just f) = Just (name, f) - toHover [] = Nothing toHover fixities = let contents = T.intercalate "\n\n" $ fixityText <$> fixities @@ -88,3 +61,102 @@ hover state plId (HoverParams (TextDocumentIdentifier uri) pos _) = pluginRespon fixityText (name, Fixity _ precedence direction) = printOutputable direction <> " " <> printOutputable precedence <> " `" <> printOutputable name <> "`" + +-- | Transferred from ghc `selectSmallestContaining` +selectSmallestContainingForFixityTree :: Span -> FixityTree -> Maybe FixityTree +selectSmallestContainingForFixityTree sp node + | sp `containsSpan` fNodeSpan node = Just node + | fNodeSpan node `containsSpan` sp = getFirst $ mconcat + [ foldMap (First . selectSmallestContainingForFixityTree sp) $ fNodeChildren node + , First (Just node) + ] + | otherwise = Nothing + +-- | Transferred from ghcide `pointCommand` +findInTree :: FixityTrees -> Position -> (FixityTree -> a) -> [a] +findInTree tree pos k = + catMaybes $ M.elems $ flip M.mapWithKey tree $ \fs ast -> + case selectSmallestContainingForFixityTree (sp fs) ast of + Nothing -> Nothing + Just ast' -> Just $ k ast' + where + sloc fs = mkRealSrcLoc fs (fromIntegral $ line+1) (fromIntegral $ cha+1) + sp fs = mkRealSrcSpan (sloc fs) (sloc fs) + line = _line pos + cha = _character pos + +data FixityTree = FNode + { fNodeSpan :: Span + , fNodeChildren :: [FixityTree] + , fNodeFixty :: [(Name, Fixity)] + } deriving (Generic) + +instance NFData FixityTree where + rnf = rwhnf + +instance Show FixityTree where + show _ = "" + +type FixityTrees = M.Map FastString FixityTree + +newtype Log = LogShake Shake.Log + +instance Pretty Log where + pretty = \case + LogShake log -> pretty log + +data GetFixity = GetFixity deriving (Show, Eq, Generic) + +instance Hashable GetFixity +instance NFData GetFixity + +type instance RuleResult GetFixity = FixityTrees + +fakeFixityTrees :: FixityTrees +fakeFixityTrees = M.empty + +-- | Convert a HieASTs to FixityTrees with fixity info gathered +hieAstsToFixitTrees :: MonadIO m => ModIface -> HscEnv -> TcGblEnv -> HieASTs a -> m FixityTrees +hieAstsToFixitTrees iface hscEnv tcGblEnv ast = + -- coerce to avoid compatibility issues. + M.mapKeysWith const coerce <$> + sequence (M.map (hieAstToFixtyTree iface hscEnv tcGblEnv) (getAsts ast)) + +-- | Convert a HieAST to FixityTree with fixity info gathered +hieAstToFixtyTree :: MonadIO m => ModIface -> HscEnv -> TcGblEnv -> HieAST a -> m FixityTree +hieAstToFixtyTree iface hscEnv tcGblEnv ast = case ast of + (Node _ span []) -> FNode span [] <$> getFixities + (Node _ span children) -> do + fixities <- getFixities + childrenFixities <- mapM (hieAstToFixtyTree iface hscEnv tcGblEnv) children + pure $ FNode span childrenFixities fixities + where + -- Names at the current ast node + names :: [Name] + names = mapMaybe eitherToMaybe $ M.keys $ getNodeIds ast + + getFixities :: MonadIO m => m [(Name, Fixity)] + getFixities = liftIO + $ fmap (filter ((/= defaultFixity) . snd) . mapMaybe pickFixity) + $ forM names $ \name -> + (\(_, fixity) -> (name, fixity)) + <$> Util.handleGhcException + (const $ pure (emptyMessages, Nothing)) + (initTcWithGbl hscEnv tcGblEnv (realSrcLocSpan $ mkRealSrcLoc "" 1 1) (lookupFixityRn name)) + + pickFixity :: (Name, Maybe Fixity) -> Maybe (Name, Fixity) + pickFixity (_, Nothing) = Nothing + pickFixity (name, Just f) = Just (name, f) + +fixityRule :: Recorder (WithPriority Log) -> Rules () +fixityRule recorder = do + define (cmapWithPrio LogShake recorder) $ \GetFixity nfp -> do + HAR{hieAst} <- use_ GetHieAst nfp + env <- hscEnv <$> use_ GhcSession nfp + tcGblEnv <- tmrTypechecked <$> use_ TypeCheck nfp + iface <- hirModIface <$> use_ GetModIface nfp + tree <- hieAstsToFixitTrees iface env tcGblEnv hieAst + pure ([], Just tree) + + -- Ensure that this plugin doesn't block on startup + addPersistentRule GetFixity $ \_ -> pure $ Just (fakeFixityTrees, idDelta, Nothing) diff --git a/plugins/hls-explicit-fixity-plugin/test/Main.hs b/plugins/hls-explicit-fixity-plugin/test/Main.hs index df2aea3787..d4b3c57372 100644 --- a/plugins/hls-explicit-fixity-plugin/test/Main.hs +++ b/plugins/hls-explicit-fixity-plugin/test/Main.hs @@ -8,7 +8,7 @@ import qualified Data.Text as T import System.FilePath plugin :: PluginDescriptor IdeState -plugin = descriptor +plugin = descriptor mempty main :: IO () main = defaultTestRunner tests @@ -39,11 +39,20 @@ tests = testGroup "Explicit fixity" , hoverTest "signature" (Position 35 2) "infixr 9 `>>>:`" , hoverTest "operator" (Position 36 2) "infixr 9 `>>>:`" , hoverTest "escape" (Position 39 2) "infixl 3 `~\\:`" + -- Ensure that there is no one extra new line in import statement + , expectFail $ hoverTest "import" (Position 2 18) "Control.Monad***" + -- Known issue, See https://github.com/haskell/haskell-language-server/pull/2973/files#r916535742 + , expectFail $ hoverTestImport "import" (Position 4 7) "infixr 9 `>>>:`" ] hoverTest :: TestName -> Position -> T.Text -> TestTree -hoverTest title pos expected = testCase title $ runSessionWithServer plugin testDataDir $ do - doc <- openDoc "Hover.hs" "haskell" +hoverTest = hoverTest' "Hover.hs" +hoverTestImport :: TestName -> Position -> T.Text -> TestTree +hoverTestImport = hoverTest' "HoverImport.hs" + +hoverTest' :: String -> TestName -> Position -> T.Text -> TestTree +hoverTest' docName title pos expected = testCase title $ runSessionWithServer plugin testDataDir $ do + doc <- openDoc docName "haskell" waitForKickDone h <- getHover doc pos let expected' = "\n" <> sectionSeparator <> expected @@ -51,7 +60,7 @@ hoverTest title pos expected = testCase title $ runSessionWithServer plugin test Nothing -> liftIO $ assertFailure "No hover" Just (Hover contents _) -> case contents of HoverContentsMS _ -> liftIO $ assertFailure "Unexpected content type" - HoverContents (MarkupContent mk txt) -> + HoverContents (MarkupContent mk txt) -> do liftIO $ assertBool ("Failed to find " <> T.unpack expected <> " in hover message") $ expected `T.isInfixOf` txt diff --git a/plugins/hls-explicit-fixity-plugin/test/testdata/Hover.hs b/plugins/hls-explicit-fixity-plugin/test/testdata/Hover.hs index 8cfa245f71..f5fd50a501 100644 --- a/plugins/hls-explicit-fixity-plugin/test/testdata/Hover.hs +++ b/plugins/hls-explicit-fixity-plugin/test/testdata/Hover.hs @@ -1,5 +1,5 @@ {-# LANGUAGE RecordWildCards #-} -module A where +module Hover where import Control.Monad import Data.Function (on) import Control.Applicative ((<|>)) diff --git a/plugins/hls-explicit-fixity-plugin/test/testdata/HoverImport.hs b/plugins/hls-explicit-fixity-plugin/test/testdata/HoverImport.hs new file mode 100644 index 0000000000..e3474eb0c3 --- /dev/null +++ b/plugins/hls-explicit-fixity-plugin/test/testdata/HoverImport.hs @@ -0,0 +1,5 @@ +module HoverImport where + +import Hover + +g = (>>>:) diff --git a/plugins/hls-explicit-fixity-plugin/test/testdata/hie.yaml b/plugins/hls-explicit-fixity-plugin/test/testdata/hie.yaml new file mode 100644 index 0000000000..824558147d --- /dev/null +++ b/plugins/hls-explicit-fixity-plugin/test/testdata/hie.yaml @@ -0,0 +1,3 @@ +cradle: + direct: + arguments: [] From 3c891e68f13028357564e527147c244c6f0f173e Mon Sep 17 00:00:00 2001 From: Lei Zhu Date: Tue, 9 Aug 2022 17:24:49 +0800 Subject: [PATCH 04/11] Run pre-commit --- exe/Plugins.hs | 2 +- plugins/hls-explicit-fixity-plugin/test/Main.hs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/exe/Plugins.hs b/exe/Plugins.hs index bb4ab25863..b3e0cdefed 100644 --- a/exe/Plugins.hs +++ b/exe/Plugins.hs @@ -93,7 +93,7 @@ import Ide.Plugin.GADT as GADT #endif #if explicitFixity -import Ide.Plugin.ExplicitFixity as ExplicitFixity +import Ide.Plugin.ExplicitFixity as ExplicitFixity #endif -- formatters diff --git a/plugins/hls-explicit-fixity-plugin/test/Main.hs b/plugins/hls-explicit-fixity-plugin/test/Main.hs index d4b3c57372..894a30201b 100644 --- a/plugins/hls-explicit-fixity-plugin/test/Main.hs +++ b/plugins/hls-explicit-fixity-plugin/test/Main.hs @@ -2,10 +2,10 @@ module Main where -import Test.Hls -import Ide.Plugin.ExplicitFixity (descriptor) -import qualified Data.Text as T -import System.FilePath +import qualified Data.Text as T +import Ide.Plugin.ExplicitFixity (descriptor) +import System.FilePath +import Test.Hls plugin :: PluginDescriptor IdeState plugin = descriptor mempty From f0bb1c1712f7812ee9fd0cf21f76719f10204a4d Mon Sep 17 00:00:00 2001 From: Lei Zhu Date: Tue, 9 Aug 2022 18:22:11 +0800 Subject: [PATCH 05/11] Compatibility: Add emptyMessages --- ghcide/src/Development/IDE/GHC/Compat/Core.hs | 4 ++-- .../src/Ide/Plugin/ExplicitFixity.hs | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/ghcide/src/Development/IDE/GHC/Compat/Core.hs b/ghcide/src/Development/IDE/GHC/Compat/Core.hs index 7d8aac3e63..222be572e6 100644 --- a/ghcide/src/Development/IDE/GHC/Compat/Core.hs +++ b/ghcide/src/Development/IDE/GHC/Compat/Core.hs @@ -382,6 +382,7 @@ module Development.IDE.GHC.Compat.Core ( module GHC.Types.Name.Cache, module GHC.Types.Name.Env, module GHC.Types.Name.Reader, + module GHC.Utils.Error, #if MIN_VERSION_ghc(9,2,0) module GHC.Types.Avail, module GHC.Types.SourceFile, @@ -392,7 +393,6 @@ module Development.IDE.GHC.Compat.Core ( module GHC.Types.Unique.Supply, module GHC.Types.Var, module GHC.Unit.Module, - module GHC.Utils.Error, #else module BasicTypes, module Class, @@ -622,7 +622,7 @@ import GHC.Unit.Module.ModIface (IfaceExport, ModIface (..), import GHC.Unit.Module.ModSummary (ModSummary (..)) #endif import GHC.Unit.State (ModuleOrigin (..)) -import GHC.Utils.Error (Severity (..)) +import GHC.Utils.Error (Severity (..), emptyMessages) import GHC.Utils.Panic hiding (try) import qualified GHC.Utils.Panic.Plain as Plain #else diff --git a/plugins/hls-explicit-fixity-plugin/src/Ide/Plugin/ExplicitFixity.hs b/plugins/hls-explicit-fixity-plugin/src/Ide/Plugin/ExplicitFixity.hs index b6f6c3fc0a..7d8a6ba2d3 100644 --- a/plugins/hls-explicit-fixity-plugin/src/Ide/Plugin/ExplicitFixity.hs +++ b/plugins/hls-explicit-fixity-plugin/src/Ide/Plugin/ExplicitFixity.hs @@ -27,7 +27,6 @@ import Development.IDE.GHC.Compat import Development.IDE.GHC.Compat.Util (FastString) import qualified Development.IDE.GHC.Compat.Util as Util import GHC.Generics (Generic) -import GHC.Utils.Error (emptyMessages) import Ide.PluginUtils (getNormalizedFilePath, handleMaybeM, pluginResponse) From 44e43cd2f85b9ca4d70adcf1a821a979fb52eb04 Mon Sep 17 00:00:00 2001 From: Lei Zhu Date: Wed, 10 Aug 2022 10:06:08 +0800 Subject: [PATCH 06/11] Remove unused ModIface --- .../src/Ide/Plugin/ExplicitFixity.hs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/plugins/hls-explicit-fixity-plugin/src/Ide/Plugin/ExplicitFixity.hs b/plugins/hls-explicit-fixity-plugin/src/Ide/Plugin/ExplicitFixity.hs index 7d8a6ba2d3..6ef74b7ae3 100644 --- a/plugins/hls-explicit-fixity-plugin/src/Ide/Plugin/ExplicitFixity.hs +++ b/plugins/hls-explicit-fixity-plugin/src/Ide/Plugin/ExplicitFixity.hs @@ -115,19 +115,19 @@ fakeFixityTrees :: FixityTrees fakeFixityTrees = M.empty -- | Convert a HieASTs to FixityTrees with fixity info gathered -hieAstsToFixitTrees :: MonadIO m => ModIface -> HscEnv -> TcGblEnv -> HieASTs a -> m FixityTrees -hieAstsToFixitTrees iface hscEnv tcGblEnv ast = +hieAstsToFixitTrees :: MonadIO m => HscEnv -> TcGblEnv -> HieASTs a -> m FixityTrees +hieAstsToFixitTrees hscEnv tcGblEnv ast = -- coerce to avoid compatibility issues. M.mapKeysWith const coerce <$> - sequence (M.map (hieAstToFixtyTree iface hscEnv tcGblEnv) (getAsts ast)) + sequence (M.map (hieAstToFixtyTree hscEnv tcGblEnv) (getAsts ast)) -- | Convert a HieAST to FixityTree with fixity info gathered -hieAstToFixtyTree :: MonadIO m => ModIface -> HscEnv -> TcGblEnv -> HieAST a -> m FixityTree -hieAstToFixtyTree iface hscEnv tcGblEnv ast = case ast of +hieAstToFixtyTree :: MonadIO m => HscEnv -> TcGblEnv -> HieAST a -> m FixityTree +hieAstToFixtyTree hscEnv tcGblEnv ast = case ast of (Node _ span []) -> FNode span [] <$> getFixities (Node _ span children) -> do fixities <- getFixities - childrenFixities <- mapM (hieAstToFixtyTree iface hscEnv tcGblEnv) children + childrenFixities <- mapM (hieAstToFixtyTree hscEnv tcGblEnv) children pure $ FNode span childrenFixities fixities where -- Names at the current ast node @@ -153,9 +153,8 @@ fixityRule recorder = do HAR{hieAst} <- use_ GetHieAst nfp env <- hscEnv <$> use_ GhcSession nfp tcGblEnv <- tmrTypechecked <$> use_ TypeCheck nfp - iface <- hirModIface <$> use_ GetModIface nfp - tree <- hieAstsToFixitTrees iface env tcGblEnv hieAst - pure ([], Just tree) + trees <- hieAstsToFixitTrees env tcGblEnv hieAst + pure ([], Just trees) -- Ensure that this plugin doesn't block on startup addPersistentRule GetFixity $ \_ -> pure $ Just (fakeFixityTrees, idDelta, Nothing) From 09acdcfb21e09ee8215ac63cf4825eb251c449ea Mon Sep 17 00:00:00 2001 From: Lei Zhu Date: Thu, 11 Aug 2022 17:12:57 +0800 Subject: [PATCH 07/11] Comment why to make this plugin a lower priority --- exe/Plugins.hs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/exe/Plugins.hs b/exe/Plugins.hs index b3e0cdefed..86dbff0a16 100644 --- a/exe/Plugins.hs +++ b/exe/Plugins.hs @@ -215,7 +215,9 @@ idePlugins recorder includeExamples = pluginDescToIdePlugins allPlugins -- (which restart the Shake build) run after everything else GhcIde.descriptors pluginRecorder #if explicitFixity - ++ [ExplicitFixity.descriptor pluginRecorder] + -- Make this plugin has a lower priority than ghcide's plugin to ensure + -- type info display first. + ++ [ExplicitFixity.descriptor pluginRecorder] #endif examplePlugins = [Example.descriptor pluginRecorder "eg" From b1941c784297ff5d0fb5d261c52e962912806c0e Mon Sep 17 00:00:00 2001 From: Lei Zhu Date: Thu, 11 Aug 2022 17:15:33 +0800 Subject: [PATCH 08/11] Provide hover content while testing fail --- plugins/hls-explicit-fixity-plugin/test/Main.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/hls-explicit-fixity-plugin/test/Main.hs b/plugins/hls-explicit-fixity-plugin/test/Main.hs index 894a30201b..52367e215c 100644 --- a/plugins/hls-explicit-fixity-plugin/test/Main.hs +++ b/plugins/hls-explicit-fixity-plugin/test/Main.hs @@ -62,7 +62,7 @@ hoverTest' docName title pos expected = testCase title $ runSessionWithServer pl HoverContentsMS _ -> liftIO $ assertFailure "Unexpected content type" HoverContents (MarkupContent mk txt) -> do liftIO - $ assertBool ("Failed to find " <> T.unpack expected <> " in hover message") + $ assertBool ("Failed to find `" <> T.unpack expected <> "` in hover message: " <> T.unpack txt) $ expected `T.isInfixOf` txt closeDoc doc From 885c632926949fc0bd6b292ed5af547870aa285e Mon Sep 17 00:00:00 2001 From: Lei Zhu Date: Thu, 11 Aug 2022 17:19:50 +0800 Subject: [PATCH 09/11] Avoid lambda --- .../src/Ide/Plugin/ExplicitFixity.hs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/plugins/hls-explicit-fixity-plugin/src/Ide/Plugin/ExplicitFixity.hs b/plugins/hls-explicit-fixity-plugin/src/Ide/Plugin/ExplicitFixity.hs index 6ef74b7ae3..3408b238bb 100644 --- a/plugins/hls-explicit-fixity-plugin/src/Ide/Plugin/ExplicitFixity.hs +++ b/plugins/hls-explicit-fixity-plugin/src/Ide/Plugin/ExplicitFixity.hs @@ -138,10 +138,11 @@ hieAstToFixtyTree hscEnv tcGblEnv ast = case ast of getFixities = liftIO $ fmap (filter ((/= defaultFixity) . snd) . mapMaybe pickFixity) $ forM names $ \name -> - (\(_, fixity) -> (name, fixity)) - <$> Util.handleGhcException - (const $ pure (emptyMessages, Nothing)) - (initTcWithGbl hscEnv tcGblEnv (realSrcLocSpan $ mkRealSrcLoc "" 1 1) (lookupFixityRn name)) + (,) name + . snd + <$> Util.handleGhcException + (const $ pure (emptyMessages, Nothing)) + (initTcWithGbl hscEnv tcGblEnv (realSrcLocSpan $ mkRealSrcLoc "" 1 1) (lookupFixityRn name)) pickFixity :: (Name, Maybe Fixity) -> Maybe (Name, Fixity) pickFixity (_, Nothing) = Nothing From 3476abe4d4a73a8cb79ddc4554e105a03916e294 Mon Sep 17 00:00:00 2001 From: Lei Zhu Date: Thu, 11 Aug 2022 17:22:58 +0800 Subject: [PATCH 10/11] 4 space indent --- .../src/Ide/Plugin/ExplicitFixity.hs | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/plugins/hls-explicit-fixity-plugin/src/Ide/Plugin/ExplicitFixity.hs b/plugins/hls-explicit-fixity-plugin/src/Ide/Plugin/ExplicitFixity.hs index 3408b238bb..8c0f0efe57 100644 --- a/plugins/hls-explicit-fixity-plugin/src/Ide/Plugin/ExplicitFixity.hs +++ b/plugins/hls-explicit-fixity-plugin/src/Ide/Plugin/ExplicitFixity.hs @@ -75,14 +75,14 @@ selectSmallestContainingForFixityTree sp node findInTree :: FixityTrees -> Position -> (FixityTree -> a) -> [a] findInTree tree pos k = catMaybes $ M.elems $ flip M.mapWithKey tree $ \fs ast -> - case selectSmallestContainingForFixityTree (sp fs) ast of - Nothing -> Nothing - Just ast' -> Just $ k ast' - where - sloc fs = mkRealSrcLoc fs (fromIntegral $ line+1) (fromIntegral $ cha+1) - sp fs = mkRealSrcSpan (sloc fs) (sloc fs) - line = _line pos - cha = _character pos + case selectSmallestContainingForFixityTree (sp fs) ast of + Nothing -> Nothing + Just ast' -> Just $ k ast' + where + sloc fs = mkRealSrcLoc fs (fromIntegral $ line+1) (fromIntegral $ cha+1) + sp fs = mkRealSrcSpan (sloc fs) (sloc fs) + line = _line pos + cha = _character pos data FixityTree = FNode { fNodeSpan :: Span @@ -94,15 +94,15 @@ instance NFData FixityTree where rnf = rwhnf instance Show FixityTree where - show _ = "" + show _ = "" type FixityTrees = M.Map FastString FixityTree newtype Log = LogShake Shake.Log instance Pretty Log where - pretty = \case - LogShake log -> pretty log + pretty = \case + LogShake log -> pretty log data GetFixity = GetFixity deriving (Show, Eq, Generic) From 75aaf5a7d30ff5c5c5a2e5df84dad8909ca679e7 Mon Sep 17 00:00:00 2001 From: Lei Zhu Date: Thu, 11 Aug 2022 17:37:33 +0800 Subject: [PATCH 11/11] Pass Text instead of Name --- .../src/Ide/Plugin/ExplicitFixity.hs | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/plugins/hls-explicit-fixity-plugin/src/Ide/Plugin/ExplicitFixity.hs b/plugins/hls-explicit-fixity-plugin/src/Ide/Plugin/ExplicitFixity.hs index 8c0f0efe57..fb4e1c1d06 100644 --- a/plugins/hls-explicit-fixity-plugin/src/Ide/Plugin/ExplicitFixity.hs +++ b/plugins/hls-explicit-fixity-plugin/src/Ide/Plugin/ExplicitFixity.hs @@ -4,6 +4,8 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TypeFamilies #-} {-# OPTIONS_GHC -Wno-deprecations #-} +{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-} +{-# HLINT ignore "Use nubOrdOn" #-} module Ide.Plugin.ExplicitFixity(descriptor) where @@ -50,16 +52,20 @@ hover state plId (HoverParams (TextDocumentIdentifier uri) pos _) = pluginRespon $ runAction "ExplicitFixity.GetFixity" state $ use GetFixity nfp -- We don't have much fixities on one position, so `nubOn` is acceptable. - pure $ toHover $ nubOn snd $ concat $ findInTree fixityTrees pos fNodeFixty + pure $ toHover $ nubOn snd $ findInTree fixityTrees pos fNodeFixty where + toHover :: [(T.Text, Fixity)] -> Maybe Hover toHover [] = Nothing toHover fixities = - let contents = T.intercalate "\n\n" $ fixityText <$> fixities + let -- Splicing fixity info + contents = T.intercalate "\n\n" $ fixityText <$> fixities + -- Append to the previous hover content contents' = "\n" <> sectionSeparator <> contents in Just $ Hover (HoverContents $ unmarkedUpContent contents') Nothing + fixityText :: (T.Text, Fixity) -> T.Text fixityText (name, Fixity _ precedence direction) = - printOutputable direction <> " " <> printOutputable precedence <> " `" <> printOutputable name <> "`" + printOutputable direction <> " " <> printOutputable precedence <> " `" <> name <> "`" -- | Transferred from ghc `selectSmallestContaining` selectSmallestContainingForFixityTree :: Span -> FixityTree -> Maybe FixityTree @@ -72,12 +78,10 @@ selectSmallestContainingForFixityTree sp node | otherwise = Nothing -- | Transferred from ghcide `pointCommand` -findInTree :: FixityTrees -> Position -> (FixityTree -> a) -> [a] +findInTree :: FixityTrees -> Position -> (FixityTree -> [a]) -> [a] findInTree tree pos k = - catMaybes $ M.elems $ flip M.mapWithKey tree $ \fs ast -> - case selectSmallestContainingForFixityTree (sp fs) ast of - Nothing -> Nothing - Just ast' -> Just $ k ast' + concat $ M.elems $ flip M.mapWithKey tree $ \fs ast -> + maybe [] k (selectSmallestContainingForFixityTree (sp fs) ast) where sloc fs = mkRealSrcLoc fs (fromIntegral $ line+1) (fromIntegral $ cha+1) sp fs = mkRealSrcSpan (sloc fs) (sloc fs) @@ -87,7 +91,7 @@ findInTree tree pos k = data FixityTree = FNode { fNodeSpan :: Span , fNodeChildren :: [FixityTree] - , fNodeFixty :: [(Name, Fixity)] + , fNodeFixty :: [(T.Text, Fixity)] } deriving (Generic) instance NFData FixityTree where @@ -134,17 +138,17 @@ hieAstToFixtyTree hscEnv tcGblEnv ast = case ast of names :: [Name] names = mapMaybe eitherToMaybe $ M.keys $ getNodeIds ast - getFixities :: MonadIO m => m [(Name, Fixity)] + getFixities :: MonadIO m => m [(T.Text, Fixity)] getFixities = liftIO $ fmap (filter ((/= defaultFixity) . snd) . mapMaybe pickFixity) $ forM names $ \name -> - (,) name + (,) (printOutputable name) . snd <$> Util.handleGhcException (const $ pure (emptyMessages, Nothing)) (initTcWithGbl hscEnv tcGblEnv (realSrcLocSpan $ mkRealSrcLoc "" 1 1) (lookupFixityRn name)) - pickFixity :: (Name, Maybe Fixity) -> Maybe (Name, Fixity) + pickFixity :: (T.Text, Maybe Fixity) -> Maybe (T.Text, Fixity) pickFixity (_, Nothing) = Nothing pickFixity (name, Just f) = Just (name, f)