From eb93be94cbe06dcef990f4c56f3af0948d044979 Mon Sep 17 00:00:00 2001 From: Oliver Madine <30090176+OliverMadine@users.noreply.github.com> Date: Tue, 17 Aug 2021 02:18:45 +0100 Subject: [PATCH 01/13] initial commit project structure initial test cases directly modify source to rename references --- .github/workflows/test.yml | 4 + cabal-ghc901.project | 3 +- cabal.project | 1 + configuration-ghc-901.nix | 2 + exe/Plugins.hs | 7 + .../session-loader/Development/IDE/Session.hs | 3 +- ghcide/src/Development/IDE/Spans/AtPoint.hs | 17 +- haskell-language-server.cabal | 14 +- plugins/hls-rename-plugin/LICENSE | 201 ++++++++++++++++++ .../hls-rename-plugin/hls-rename-plugin.cabal | 52 +++++ .../src/Ide/Plugin/Rename.hs | 138 ++++++++++++ plugins/hls-rename-plugin/test/Main.hs | 63 ++++++ .../test/testdata/DataConstructor.expected.hs | 4 + .../test/testdata/DataConstructor.hs | 4 + .../testdata/ExportedFunction.expected.hs | 5 + .../test/testdata/ExportedFunction.hs | 5 + .../hls-rename-plugin/test/testdata/Foo.hs | 4 + .../testdata/FunctionArgument.expected.hs | 4 + .../test/testdata/FunctionArgument.hs | 4 + .../test/testdata/FunctionName.expected.hs | 6 + .../test/testdata/FunctionName.hs | 4 +- .../test/testdata/Gadt.expected.hs | 17 ++ .../hls-rename-plugin/test/testdata/Gadt.hs | 17 ++ .../test/testdata/HiddenFunction.expected.hs | 4 + .../test/testdata/HiddenFunction.hs | 4 + .../test/testdata/ImportHiding.expected.hs | 4 + .../test/testdata/ImportHiding.hs | 4 + .../testdata/ImportedFunction.expected.hs | 4 + .../test/testdata/ImportedFunction.hs | 4 + .../test/testdata/Indentation.expected.hs | 4 + .../test/testdata/Indentation.hs | 4 + .../test/testdata/QualifiedAs.expected.hs | 4 + .../test/testdata/QualifiedAs.hs | 4 + .../testdata/QualifiedFunction.expected.hs | 4 + .../test/testdata/QualifiedFunction.hs | 4 + .../testdata/QualifiedShadowing.expected.hs | 7 + .../test/testdata/QualifiedShadowing.hs | 7 + .../test/testdata/RecordField.expected.hs | 7 + .../test/testdata/RecordField.hs | 7 + .../test/testdata/ShadowedName.expected.hs | 4 + .../test/testdata/ShadowedName.hs | 4 + .../test/testdata/TypeConstructor.expected.hs | 5 + .../test/testdata/TypeConstructor.hs | 5 + .../test/testdata/TypeVariable.expected.hs | 2 + .../test/testdata/TypeVariable.hs | 2 + .../hls-rename-plugin/test/testdata/hie.yaml | 20 ++ .../src/Ide/Plugin/Retrie.hs | 2 +- stack-8.10.2.yaml | 3 +- stack-8.10.3.yaml | 3 +- stack-8.10.4.yaml | 3 +- stack-8.10.5.yaml | 1 + stack-8.8.3.yaml | 3 +- stack-8.8.4.yaml | 3 +- stack-9.0.1.yaml | 2 + stack.yaml | 3 +- test/functional/Main.hs | 2 - test/functional/Rename.hs | 23 -- test/utils/Test/Hls/Flags.hs | 8 + 58 files changed, 712 insertions(+), 41 deletions(-) create mode 100644 plugins/hls-rename-plugin/LICENSE create mode 100644 plugins/hls-rename-plugin/hls-rename-plugin.cabal create mode 100644 plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs create mode 100644 plugins/hls-rename-plugin/test/Main.hs create mode 100644 plugins/hls-rename-plugin/test/testdata/DataConstructor.expected.hs create mode 100644 plugins/hls-rename-plugin/test/testdata/DataConstructor.hs create mode 100644 plugins/hls-rename-plugin/test/testdata/ExportedFunction.expected.hs create mode 100644 plugins/hls-rename-plugin/test/testdata/ExportedFunction.hs create mode 100644 plugins/hls-rename-plugin/test/testdata/Foo.hs create mode 100644 plugins/hls-rename-plugin/test/testdata/FunctionArgument.expected.hs create mode 100644 plugins/hls-rename-plugin/test/testdata/FunctionArgument.hs create mode 100644 plugins/hls-rename-plugin/test/testdata/FunctionName.expected.hs rename test/testdata/rename/Rename.hs => plugins/hls-rename-plugin/test/testdata/FunctionName.hs (57%) create mode 100644 plugins/hls-rename-plugin/test/testdata/Gadt.expected.hs create mode 100644 plugins/hls-rename-plugin/test/testdata/Gadt.hs create mode 100644 plugins/hls-rename-plugin/test/testdata/HiddenFunction.expected.hs create mode 100644 plugins/hls-rename-plugin/test/testdata/HiddenFunction.hs create mode 100644 plugins/hls-rename-plugin/test/testdata/ImportHiding.expected.hs create mode 100644 plugins/hls-rename-plugin/test/testdata/ImportHiding.hs create mode 100644 plugins/hls-rename-plugin/test/testdata/ImportedFunction.expected.hs create mode 100644 plugins/hls-rename-plugin/test/testdata/ImportedFunction.hs create mode 100644 plugins/hls-rename-plugin/test/testdata/Indentation.expected.hs create mode 100644 plugins/hls-rename-plugin/test/testdata/Indentation.hs create mode 100644 plugins/hls-rename-plugin/test/testdata/QualifiedAs.expected.hs create mode 100644 plugins/hls-rename-plugin/test/testdata/QualifiedAs.hs create mode 100644 plugins/hls-rename-plugin/test/testdata/QualifiedFunction.expected.hs create mode 100644 plugins/hls-rename-plugin/test/testdata/QualifiedFunction.hs create mode 100644 plugins/hls-rename-plugin/test/testdata/QualifiedShadowing.expected.hs create mode 100644 plugins/hls-rename-plugin/test/testdata/QualifiedShadowing.hs create mode 100644 plugins/hls-rename-plugin/test/testdata/RecordField.expected.hs create mode 100644 plugins/hls-rename-plugin/test/testdata/RecordField.hs create mode 100644 plugins/hls-rename-plugin/test/testdata/ShadowedName.expected.hs create mode 100644 plugins/hls-rename-plugin/test/testdata/ShadowedName.hs create mode 100644 plugins/hls-rename-plugin/test/testdata/TypeConstructor.expected.hs create mode 100644 plugins/hls-rename-plugin/test/testdata/TypeConstructor.hs create mode 100644 plugins/hls-rename-plugin/test/testdata/TypeVariable.expected.hs create mode 100644 plugins/hls-rename-plugin/test/testdata/TypeVariable.hs create mode 100644 plugins/hls-rename-plugin/test/testdata/hie.yaml delete mode 100644 test/functional/Rename.hs diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3a02d2763d..dd8f575818 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -208,3 +208,7 @@ jobs: - if: ${{ needs.pre_job.outputs.should_skip != 'true' && matrix.test}} name: Test hls-call-hierarchy-plugin test suite run: cabal test hls-call-hierarchy-plugin --test-options="-j1 --rerun-update" || cabal test hls-call-hierarchy-plugin --test-options="-j1 --rerun" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-call-hierarchy-plugin --test-options="-j1 --rerun" + + - if: ${{ needs.pre_job.outputs.should_skip != 'true' && matrix.test && matrix.ghc >= '8.8.0'}} + name: Test hls-rename-plugin test suite + run: cabal test hls-rename-plugin --test-options="-j1 --rerun-update" || cabal test hls-rename-plugin --test-options="-j1 --rerun" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-call-hierarchy-plugin --rename-options="-j1 --rerun" diff --git a/cabal-ghc901.project b/cabal-ghc901.project index 5cd90914b3..aa84008ecb 100644 --- a/cabal-ghc901.project +++ b/cabal-ghc901.project @@ -15,6 +15,7 @@ packages: ./plugins/hls-explicit-imports-plugin ./plugins/hls-refine-imports-plugin ./plugins/hls-hlint-plugin + ./plugins/hls-rename-plugin ./plugins/hls-retrie-plugin ./plugins/hls-haddock-comments-plugin -- ./plugins/hls-splice-plugin @@ -79,7 +80,7 @@ index-state: 2021-08-08T02:21:16Z constraints: -- These plugins doesn't work on GHC9 yet - haskell-language-server -brittany -class -fourmolu -splice -stylishhaskell -tactic -refineImports + haskell-language-server -brittany -class -fourmolu -splice -stylishhaskell -tactic -refineImports -rename allow-newer: diff --git a/cabal.project b/cabal.project index b0fbffe75d..e375335537 100644 --- a/cabal.project +++ b/cabal.project @@ -15,6 +15,7 @@ packages: ./plugins/hls-explicit-imports-plugin ./plugins/hls-refine-imports-plugin ./plugins/hls-hlint-plugin + ./plugins/hls-rename-plugin ./plugins/hls-retrie-plugin ./plugins/hls-haddock-comments-plugin ./plugins/hls-splice-plugin diff --git a/configuration-ghc-901.nix b/configuration-ghc-901.nix index 7f3d38554c..aca5b1a5b1 100644 --- a/configuration-ghc-901.nix +++ b/configuration-ghc-901.nix @@ -7,6 +7,7 @@ let "hls-brittany-plugin" "hls-stylish-haskell-plugin" "hls-fourmolu-plugin" + "hls-rename-plugin" "hls-splice-plugin" "hls-class-plugin" "hls-refine-imports-plugin" @@ -91,6 +92,7 @@ let "-f-brittany" "-f-class" "-f-fourmolu" + "-f-rename" "-f-splice" "-f-stylishhaskell" "-f-tactic" diff --git a/exe/Plugins.hs b/exe/Plugins.hs index 8b2c3178d6..5e4a6165d9 100644 --- a/exe/Plugins.hs +++ b/exe/Plugins.hs @@ -37,6 +37,10 @@ import Ide.Plugin.ExplicitImports as ExplicitImports import Ide.Plugin.RefineImports as RefineImports #endif +#if rename +import Ide.Plugin.Rename as Rename +#endif + #if retrie import Ide.Plugin.Retrie as Retrie #endif @@ -115,6 +119,9 @@ idePlugins includeExamples = pluginDescToIdePlugins allPlugins #if stylishHaskell StylishHaskell.descriptor "stylish-haskell" : #endif +#if rename + Rename.descriptor "rename" : +#endif #if retrie Retrie.descriptor "retrie" : #endif diff --git a/ghcide/session-loader/Development/IDE/Session.hs b/ghcide/session-loader/Development/IDE/Session.hs index b5a7ba893c..b769ed916a 100644 --- a/ghcide/session-loader/Development/IDE/Session.hs +++ b/ghcide/session-loader/Development/IDE/Session.hs @@ -501,9 +501,8 @@ cradleToOptsAndLibDir :: Show a => Cradle a -> FilePath -> IO (Either [CradleError] (ComponentOptions, FilePath)) cradleToOptsAndLibDir cradle file = do -- Start off by getting the session options - let showLine s = hPutStrLn stderr ("> " ++ s) hPutStrLn stderr $ "Output from setting up the cradle " <> show cradle - cradleRes <- runCradle (cradleOptsProg cradle) showLine file + cradleRes <- HieBios.getCompilerOptions file cradle case cradleRes of CradleSuccess r -> do -- Now get the GHC lib dir diff --git a/ghcide/src/Development/IDE/Spans/AtPoint.hs b/ghcide/src/Development/IDE/Spans/AtPoint.hs index da49cc1a54..2c878ebe1b 100644 --- a/ghcide/src/Development/IDE/Spans/AtPoint.hs +++ b/ghcide/src/Development/IDE/Spans/AtPoint.hs @@ -16,6 +16,9 @@ module Development.IDE.Spans.AtPoint ( , computeTypeReferences , FOIReferences(..) , defRowToSymbolInfo + , getAstNamesAtPoint + , toCurrentLocation + , rowToLoc ) where import Development.IDE.GHC.Error @@ -90,8 +93,7 @@ foiReferencesAtPoint file pos (FOIReferences asts) = case HM.lookup file asts of Nothing -> ([],[],[]) Just (HAR _ hf _ _ _,mapping) -> - let posFile = fromMaybe pos $ fromCurrentPosition mapping pos - names = concat $ pointCommand hf posFile (rights . M.keys . getNodeIds) + let names = getAstNamesAtPoint hf pos mapping adjustedLocs = HM.foldr go [] asts go (HAR _ _ rf tr _, mapping) xs = refs ++ typerefs ++ xs where @@ -99,9 +101,18 @@ foiReferencesAtPoint file pos (FOIReferences asts) = $ concat $ mapMaybe (\n -> M.lookup (Right n) rf) names typerefs = mapMaybe (toCurrentLocation mapping . realSrcSpanToLocation) $ concat $ mapMaybe (`M.lookup` tr) names - toCurrentLocation mapping (Location uri range) = Location uri <$> toCurrentRange mapping range in (names, adjustedLocs,map fromNormalizedFilePath $ HM.keys asts) +getAstNamesAtPoint :: HieASTs a -> Position -> PositionMapping -> [Name] +getAstNamesAtPoint hf pos mapping = + concat $ pointCommand hf posFile (rights . M.keys . getNodeIds) + where + posFile = fromMaybe pos $ fromCurrentPosition mapping pos + +toCurrentLocation :: PositionMapping -> Location -> Maybe Location +toCurrentLocation mapping (Location uri range) = + Location uri <$> toCurrentRange mapping range + referencesAtPoint :: MonadIO m => HieDb diff --git a/haskell-language-server.cabal b/haskell-language-server.cabal index fd8c5473c2..3f8f6095a4 100644 --- a/haskell-language-server.cabal +++ b/haskell-language-server.cabal @@ -131,6 +131,11 @@ flag refineImports default: True manual: True +flag rename + description: Enable rename plugin + default: True + manual: True + flag retrie description: Enable retrie plugin default: True @@ -223,6 +228,11 @@ common refineImports build-depends: hls-refine-imports-plugin ^>=1.0.0.0 cpp-options: -DrefineImports +common rename + if impl(ghc >= 8.8) && (flag(rename) || flag(all-plugins)) + build-depends: hls-rename-plugin ^>= 1.0.0.0 + cpp-options: -Drename + common retrie if flag(retrie) || flag(all-plugins) build-depends: hls-retrie-plugin ^>=1.0.0.1 @@ -290,6 +300,7 @@ executable haskell-language-server , eval , importLens , refineImports + , rename , retrie , tactic , hlint @@ -424,7 +435,6 @@ test-suite func-test Highlight Progress Reference - Rename Symbol TypeDefinition Test.Hls.Command @@ -447,6 +457,8 @@ test-suite func-test cpp-options: -Deval if flag(importLens) || flag(all-plugins) cpp-options: -DimportLens + if impl(ghc > 8.8) && (flag(rename) || flag(all-plugins)) + cpp-options: -Drename if flag(retrie) || flag(all-plugins) cpp-options: -Dretrie if flag(tactic) || flag(all-plugins) diff --git a/plugins/hls-rename-plugin/LICENSE b/plugins/hls-rename-plugin/LICENSE new file mode 100644 index 0000000000..261eeb9e9f --- /dev/null +++ b/plugins/hls-rename-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-rename-plugin/hls-rename-plugin.cabal b/plugins/hls-rename-plugin/hls-rename-plugin.cabal new file mode 100644 index 0000000000..87a1ced1b7 --- /dev/null +++ b/plugins/hls-rename-plugin/hls-rename-plugin.cabal @@ -0,0 +1,52 @@ +cabal-version: 2.4 +name: hls-rename-plugin +version: 1.0.0.0 +synopsis: Rename plugin for Haskell Language Server +description: + Please see the README on GitHub at + +license: Apache-2.0 +license-file: LICENSE +author: Oliver Madine +maintainer: madine.oliver@gmail.com +category: Development +build-type: Simple +extra-source-files: + LICENSE + test/testdata/*.hs + test/testdata/*.yaml + +library + exposed-modules: Ide.Plugin.Rename + hs-source-dirs: src + build-depends: + , aeson + , base >=4.12 && <5 + , lsp-types + , lsp + , hls-retrie-plugin + , hls-plugin-api ^>=1.2 + , retrie >=0.1.1.1 + , ghcide >=1.4 && <1.5 + , transformers + , text + , unordered-containers + , containers + , ghc + , hiedb + , ghc-exactprint + , syb + + default-language: Haskell2010 + +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-rename-plugin + , hls-test-utils >=1.0 && <1.2 diff --git a/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs b/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs new file mode 100644 index 0000000000..59ed0722a9 --- /dev/null +++ b/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs @@ -0,0 +1,138 @@ +{-# LANGUAGE CPP #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE NamedFieldPuns #-} + +module Ide.Plugin.Rename (descriptor) where +import Control.Monad +import Control.Monad.IO.Class (MonadIO (liftIO)) +import Control.Monad.Trans.Except +import qualified Data.Bifunctor +import Data.Containers.ListUtils +import Data.Generics +import qualified Data.HashMap.Strict as HM +import Data.List +import qualified Data.Map as M +import Data.Maybe +import qualified Data.Text as T +import Development.IDE hiding (pluginHandlers) +import Development.IDE.Core.PositionMapping +import Development.IDE.Core.Shake +import Development.IDE.GHC.Compat +import Development.IDE.GHC.ExactPrint +import Development.IDE.Spans.AtPoint +import HieDb.Query +import Ide.Plugin.Config +import Ide.Plugin.Retrie hiding (descriptor) +import Ide.PluginUtils +import Ide.Types +import Language.LSP.Server +import Language.LSP.Types +import Retrie hiding (HasSrcSpan, + HsModule, getLoc) + +descriptor :: PluginId -> PluginDescriptor IdeState +descriptor pluginId = (defaultPluginDescriptor pluginId) { + pluginHandlers = mkPluginHandler STextDocumentRename renameProvider +} + +renameProvider :: PluginMethodHandler IdeState TextDocumentRename +renameProvider state pluginId (RenameParams (TextDocumentIdentifier uri) pos _prog newNameText) = response $ do + nfp <- safeGetNfp uri + oldName <- (handleMaybe "error: could not find name at pos" . listToMaybe) =<< + getNamesAtPos state pos nfp + refs <- refsAtName state nfp oldName + refFiles <- mapM safeGetNfp (nub [uri | Location uri _ <- refs]) + let newNameStr = T.unpack newNameText + newOccName = mkTcOcc newNameStr + + nfpEdits <- mapMToSnd (getSrcEdits state (renameRefs refs (mkTcOcc newNameStr))) refFiles + let uriEdits = HM.fromList $ map (Data.Bifunctor.first nfpToUri) nfpEdits + + pure $ WorkspaceEdit (Just uriEdits) Nothing Nothing + +------------------------------------------------------------------------------- +-- Source renaming + +getSrcEdits :: + IdeState +#if MIN_VERSION_ghc(9,0,1) + -> (HsModule -> HsModule) +#else + -> (HsModule GhcPs -> HsModule GhcPs) +#endif + -> NormalizedFilePath + -> ExceptT String (LspT Config IO) (List TextEdit) +getSrcEdits state updateMod nfp = do + annPs <- handleMaybeM "error: parsed source" $ + liftIO $ runAction + "Rename.GetAnnotatedParsedModule" + state + (use GetAnnotatedParsedSource nfp) + let src = T.pack $ printA annPs + res = T.pack $ printA $ (fmap . fmap) updateMod annPs + pure $ makeDiffTextEdit src res + +renameRefs :: + [Location] + -> OccName +#if MIN_VERSION_ghc(9,0,1) + -> HsModule + -> HsModule +#else + -> HsModule GhcPs + -> HsModule GhcPs +#endif +renameRefs refs newOccName = everywhere $ mkT replace + where + replace :: Located RdrName -> Located RdrName + replace lOldRdrName + | isRef refs lOldRdrName = fmap (const newRdrName) lOldRdrName + | otherwise = lOldRdrName + where + newRdrName = + case unLoc lOldRdrName of + Qual modName _ -> Qual modName newOccName + _ -> Unqual newOccName + + isRef :: HasSrcSpan a => [Location] -> a -> Bool + isRef refs = (`elem` refs) . fromJust . srcSpanToLocation . getLoc + +------------------------------------------------------------ +-- reference finding + +refsAtName :: IdeState -> NormalizedFilePath -> Name -> ExceptT [Char] (LspT Config IO) [Location] +refsAtName state nfp name = do + ShakeExtras{hiedb} <- liftIO $ runAction "Rename.HieDb" state getShakeExtras + ast <- handleMaybeM "error: ast" $ liftIO $ runAction "" state $ useWithStale GetHieAst nfp + fileRefs <- handleMaybe "error: name references" $ getNameAstLocations name ast + let mod = nameModule_maybe name + dbRefs <- liftIO $ mapMaybe rowToLoc <$> findReferences + hiedb + True + (nameOccName name) + (moduleName <$> mod) + (moduleUnitId <$> mod) + [fromNormalizedFilePath nfp] + pure $ nubOrd $ fileRefs ++ dbRefs + +getNameAstLocations :: Name -> (HieAstResult, PositionMapping) -> Maybe [Location] +getNameAstLocations name (HAR _ _ rm _ _, mapping) = + mapMaybe (toCurrentLocation mapping . realSrcSpanToLocation . fst) <$> M.lookup (Right name) rm + +------------------------------------------------------------------------------- +-- util + +safeGetNfp :: (Monad m) => Uri -> ExceptT String m NormalizedFilePath +safeGetNfp uri = handleMaybe "error: uri" $ toNormalizedFilePath <$> uriToFilePath uri + +getNamesAtPos :: IdeState -> Position -> NormalizedFilePath -> ExceptT String (LspT Config IO) [Name] +getNamesAtPos state pos nfp = do + (HAR{hieAst}, mapping) <- handleMaybeM "error: ast" $ liftIO $ + runAction "Rename.GetHieAst" state $ useWithStale GetHieAst nfp + pure $ getAstNamesAtPoint hieAst pos mapping + +mapMToSnd :: Monad f => (a -> f b) -> [a] -> f [(a, b)] +mapMToSnd = liftM2 (<$>) zip . mapM + +nfpToUri :: NormalizedFilePath -> Uri +nfpToUri = filePathToUri . fromNormalizedFilePath diff --git a/plugins/hls-rename-plugin/test/Main.hs b/plugins/hls-rename-plugin/test/Main.hs new file mode 100644 index 0000000000..c562b376a3 --- /dev/null +++ b/plugins/hls-rename-plugin/test/Main.hs @@ -0,0 +1,63 @@ +{-# LANGUAGE OverloadedStrings #-} + +module Main (main) where + +import qualified Ide.Plugin.Rename as Rename +import System.FilePath +import Test.Hls + +main :: IO () +main = defaultTestRunner tests + +renamePlugin :: PluginDescriptor IdeState +renamePlugin = Rename.descriptor "rename" + +tests :: TestTree +tests = testGroup "rename" + [ testGroup "Top-level renames" + [ goldenWithRename "function name" "FunctionName" $ \doc -> do + rename doc (Position 3 1) "baz" + , goldenWithRename "GADT" "Gadt" $ \doc -> do + rename doc (Position 6 37) "Expr" + , goldenWithRename "imported function" "ImportedFunction" $ \doc -> do + rename doc (Position 3 8) "baz" + , goldenWithRename "exported function" "ExportedFunction" $ \doc -> do + rename doc (Position 2 1) "quux" + , goldenWithRename "hidden function" "HiddenFunction" $ \doc -> do + rename doc (Position 0 32) "quux" + , goldenWithRename "allign indentation" "Indentation" $ \doc -> do + rename doc (Position 0 2) "fooBarQuux" + , goldenWithRename "shadowed name" "ShadowedName" $ \doc -> do + rename doc (Position 1 1) "baz" + , goldenWithRename "qualified function" "QualifiedFunction" $ \doc -> do + rename doc (Position 3 12) "baz" + , goldenWithRename "type constructor" "TypeConstructor" $ \doc -> do + rename doc (Position 2 17) "BinaryTree" + , goldenWithRename "data constructor" "DataConstructor" $ \doc -> do + rename doc (Position 0 15) "Op" + , goldenWithRename "import hiding" "ImportHiding" $ \doc -> do + rename doc (Position 0 22) "hiddenFoo" + , goldenWithRename "qualified as" "QualifiedAs" $ \doc -> do + rename doc (Position 3 10) "baz" + , goldenWithRename "qualified shadowing" "QualifiedShadowing" $ \doc -> do + rename doc (Position 3 12) "foobar" + ] + , testGroup "non Top-level renames" + [ goldenWithRename "function argument" "FunctionArgument" $ \doc -> do + rename doc (Position 3 4) "y" + , goldenWithRename "record field" "RecordField" $ \doc -> do + rename doc (Position 6 9) "number" + , goldenWithRename "type variable" "TypeVariable" $ \doc -> do + rename doc (Position 0 13) "b" + ] + ] + +goldenWithRename :: TestName -> FilePath -> (TextDocumentIdentifier -> Session ()) -> TestTree +goldenWithRename title path act = + goldenWithHaskellDoc renamePlugin title testDataDir path "expected" "hs" $ \doc -> do + waitForProgressDone + waitForProgressDone + act doc + +testDataDir :: FilePath +testDataDir = "test" "testdata" diff --git a/plugins/hls-rename-plugin/test/testdata/DataConstructor.expected.hs b/plugins/hls-rename-plugin/test/testdata/DataConstructor.expected.hs new file mode 100644 index 0000000000..d1ee10a6d1 --- /dev/null +++ b/plugins/hls-rename-plugin/test/testdata/DataConstructor.expected.hs @@ -0,0 +1,4 @@ +data Expr = Op Int Int + +plus :: Expr -> Expr +plus (Op n m) = Op (n + m) 0 diff --git a/plugins/hls-rename-plugin/test/testdata/DataConstructor.hs b/plugins/hls-rename-plugin/test/testdata/DataConstructor.hs new file mode 100644 index 0000000000..b614d72291 --- /dev/null +++ b/plugins/hls-rename-plugin/test/testdata/DataConstructor.hs @@ -0,0 +1,4 @@ +data Expr = Apply Int Int + +plus :: Expr -> Expr +plus (Apply n m) = Apply (n + m) 0 diff --git a/plugins/hls-rename-plugin/test/testdata/ExportedFunction.expected.hs b/plugins/hls-rename-plugin/test/testdata/ExportedFunction.expected.hs new file mode 100644 index 0000000000..568edb36db --- /dev/null +++ b/plugins/hls-rename-plugin/test/testdata/ExportedFunction.expected.hs @@ -0,0 +1,5 @@ +module ExportedFunction (quux) where + +quux :: Num p => [a] -> p +quux [] = 0 +quux xs = 1 diff --git a/plugins/hls-rename-plugin/test/testdata/ExportedFunction.hs b/plugins/hls-rename-plugin/test/testdata/ExportedFunction.hs new file mode 100644 index 0000000000..3adb72dc9f --- /dev/null +++ b/plugins/hls-rename-plugin/test/testdata/ExportedFunction.hs @@ -0,0 +1,5 @@ +module ExportedFunction (foo) where + +foo :: Num p => [a] -> p +foo [] = 0 +foo xs = 1 diff --git a/plugins/hls-rename-plugin/test/testdata/Foo.hs b/plugins/hls-rename-plugin/test/testdata/Foo.hs new file mode 100644 index 0000000000..c4850149b4 --- /dev/null +++ b/plugins/hls-rename-plugin/test/testdata/Foo.hs @@ -0,0 +1,4 @@ +module Foo where + +foo :: Int -> Int +foo x = 0 diff --git a/plugins/hls-rename-plugin/test/testdata/FunctionArgument.expected.hs b/plugins/hls-rename-plugin/test/testdata/FunctionArgument.expected.hs new file mode 100644 index 0000000000..bd8d83b334 --- /dev/null +++ b/plugins/hls-rename-plugin/test/testdata/FunctionArgument.expected.hs @@ -0,0 +1,4 @@ +module FunctionArgument where + +foo :: Int -> Int +foo y = y + 1 diff --git a/plugins/hls-rename-plugin/test/testdata/FunctionArgument.hs b/plugins/hls-rename-plugin/test/testdata/FunctionArgument.hs new file mode 100644 index 0000000000..a6006e6fac --- /dev/null +++ b/plugins/hls-rename-plugin/test/testdata/FunctionArgument.hs @@ -0,0 +1,4 @@ +module FunctionArgument where + +foo :: Int -> Int +foo x = x + 1 diff --git a/plugins/hls-rename-plugin/test/testdata/FunctionName.expected.hs b/plugins/hls-rename-plugin/test/testdata/FunctionName.expected.hs new file mode 100644 index 0000000000..a8cb8fc3bd --- /dev/null +++ b/plugins/hls-rename-plugin/test/testdata/FunctionName.expected.hs @@ -0,0 +1,6 @@ +main = do + x <- return $ baz 42 + return (baz x) +baz :: Int -> Int +baz x = x + 1 +bar = (+ 1) . baz diff --git a/test/testdata/rename/Rename.hs b/plugins/hls-rename-plugin/test/testdata/FunctionName.hs similarity index 57% rename from test/testdata/rename/Rename.hs rename to plugins/hls-rename-plugin/test/testdata/FunctionName.hs index 19f566795f..d13fa2c286 100644 --- a/test/testdata/rename/Rename.hs +++ b/plugins/hls-rename-plugin/test/testdata/FunctionName.hs @@ -1,6 +1,6 @@ main = do - x <- return $ foo 42 - return (foo x) + x <- return $ foo 42 + return (foo x) foo :: Int -> Int foo x = x + 1 bar = (+ 1) . foo diff --git a/plugins/hls-rename-plugin/test/testdata/Gadt.expected.hs b/plugins/hls-rename-plugin/test/testdata/Gadt.expected.hs new file mode 100644 index 0000000000..65115d42d7 --- /dev/null +++ b/plugins/hls-rename-plugin/test/testdata/Gadt.expected.hs @@ -0,0 +1,17 @@ +{-# LANGUAGE GADTs #-} + +data Expr a where + Number :: Int -> Expr Int + Boolean :: Bool -> Expr Bool + Not :: Expr Bool -> Expr Bool + Even :: Expr Int -> Expr Bool + Add :: Enum a => Expr a -> Expr a -> Expr Int + Max :: Ord a => Expr a -> Expr a -> Expr a + +evaluate :: Expr a -> a +evaluate (Number n) = n +evaluate (Boolean p) = p +evaluate (Add n m) = fromEnum (evaluate n) + fromEnum (evaluate m) +evaluate (Even n) = even $ evaluate n +evaluate (Not p) = not $ evaluate p +evaluate (Max x y) = max (evaluate x) (evaluate y) diff --git a/plugins/hls-rename-plugin/test/testdata/Gadt.hs b/plugins/hls-rename-plugin/test/testdata/Gadt.hs new file mode 100644 index 0000000000..408f516900 --- /dev/null +++ b/plugins/hls-rename-plugin/test/testdata/Gadt.hs @@ -0,0 +1,17 @@ +{-# LANGUAGE GADTs #-} + +data Expression a where + Number :: Int -> Expression Int + Boolean :: Bool -> Expression Bool + Not :: Expression Bool -> Expression Bool + Even :: Expression Int -> Expression Bool + Add :: Enum a => Expression a -> Expression a -> Expression Int + Max :: Ord a => Expression a -> Expression a -> Expression a + +evaluate :: Expression a -> a +evaluate (Number n) = n +evaluate (Boolean p) = p +evaluate (Add n m) = fromEnum (evaluate n) + fromEnum (evaluate m) +evaluate (Even n) = even $ evaluate n +evaluate (Not p) = not $ evaluate p +evaluate (Max x y) = max (evaluate x) (evaluate y) diff --git a/plugins/hls-rename-plugin/test/testdata/HiddenFunction.expected.hs b/plugins/hls-rename-plugin/test/testdata/HiddenFunction.expected.hs new file mode 100644 index 0000000000..3195291c66 --- /dev/null +++ b/plugins/hls-rename-plugin/test/testdata/HiddenFunction.expected.hs @@ -0,0 +1,4 @@ +import Foo hiding (quux) + +foo :: Int -> Int +foo x = 0 diff --git a/plugins/hls-rename-plugin/test/testdata/HiddenFunction.hs b/plugins/hls-rename-plugin/test/testdata/HiddenFunction.hs new file mode 100644 index 0000000000..eacb9d1a4c --- /dev/null +++ b/plugins/hls-rename-plugin/test/testdata/HiddenFunction.hs @@ -0,0 +1,4 @@ +import Foo hiding (foo) + +foo :: Int -> Int +foo x = 0 diff --git a/plugins/hls-rename-plugin/test/testdata/ImportHiding.expected.hs b/plugins/hls-rename-plugin/test/testdata/ImportHiding.expected.hs new file mode 100644 index 0000000000..e1b600aa1c --- /dev/null +++ b/plugins/hls-rename-plugin/test/testdata/ImportHiding.expected.hs @@ -0,0 +1,4 @@ +import Foo hiding (hiddenFoo) + +foo :: Int -> Int +foo _ = 5 diff --git a/plugins/hls-rename-plugin/test/testdata/ImportHiding.hs b/plugins/hls-rename-plugin/test/testdata/ImportHiding.hs new file mode 100644 index 0000000000..c14099e68b --- /dev/null +++ b/plugins/hls-rename-plugin/test/testdata/ImportHiding.hs @@ -0,0 +1,4 @@ +import Foo hiding (foo) + +foo :: Int -> Int +foo _ = 5 diff --git a/plugins/hls-rename-plugin/test/testdata/ImportedFunction.expected.hs b/plugins/hls-rename-plugin/test/testdata/ImportedFunction.expected.hs new file mode 100644 index 0000000000..8f0cbcf888 --- /dev/null +++ b/plugins/hls-rename-plugin/test/testdata/ImportedFunction.expected.hs @@ -0,0 +1,4 @@ +import Foo (baz) + +bar :: Int -> Int +bar = baz diff --git a/plugins/hls-rename-plugin/test/testdata/ImportedFunction.hs b/plugins/hls-rename-plugin/test/testdata/ImportedFunction.hs new file mode 100644 index 0000000000..56361fc64b --- /dev/null +++ b/plugins/hls-rename-plugin/test/testdata/ImportedFunction.hs @@ -0,0 +1,4 @@ +import Foo (foo) + +bar :: Int -> Int +bar = foo diff --git a/plugins/hls-rename-plugin/test/testdata/Indentation.expected.hs b/plugins/hls-rename-plugin/test/testdata/Indentation.expected.hs new file mode 100644 index 0000000000..9033a89d87 --- /dev/null +++ b/plugins/hls-rename-plugin/test/testdata/Indentation.expected.hs @@ -0,0 +1,4 @@ +fooBarQuux :: Maybe Integer +fooBarQuux = do x <- Just 5 + t <- Just 10 + pure $ x + t diff --git a/plugins/hls-rename-plugin/test/testdata/Indentation.hs b/plugins/hls-rename-plugin/test/testdata/Indentation.hs new file mode 100644 index 0000000000..aa121ac984 --- /dev/null +++ b/plugins/hls-rename-plugin/test/testdata/Indentation.hs @@ -0,0 +1,4 @@ +foo :: Maybe Integer +foo = do x <- Just 5 + t <- Just 10 + pure $ x + t diff --git a/plugins/hls-rename-plugin/test/testdata/QualifiedAs.expected.hs b/plugins/hls-rename-plugin/test/testdata/QualifiedAs.expected.hs new file mode 100644 index 0000000000..a864119ef2 --- /dev/null +++ b/plugins/hls-rename-plugin/test/testdata/QualifiedAs.expected.hs @@ -0,0 +1,4 @@ +import qualified Foo as F + +bar :: Int -> Int +bar = F.baz diff --git a/plugins/hls-rename-plugin/test/testdata/QualifiedAs.hs b/plugins/hls-rename-plugin/test/testdata/QualifiedAs.hs new file mode 100644 index 0000000000..022b2f8e31 --- /dev/null +++ b/plugins/hls-rename-plugin/test/testdata/QualifiedAs.hs @@ -0,0 +1,4 @@ +import qualified Foo as F + +bar :: Int -> Int +bar = F.foo diff --git a/plugins/hls-rename-plugin/test/testdata/QualifiedFunction.expected.hs b/plugins/hls-rename-plugin/test/testdata/QualifiedFunction.expected.hs new file mode 100644 index 0000000000..808c12b066 --- /dev/null +++ b/plugins/hls-rename-plugin/test/testdata/QualifiedFunction.expected.hs @@ -0,0 +1,4 @@ +import qualified Foo + +bar :: Int -> Int +bar = Foo.baz diff --git a/plugins/hls-rename-plugin/test/testdata/QualifiedFunction.hs b/plugins/hls-rename-plugin/test/testdata/QualifiedFunction.hs new file mode 100644 index 0000000000..01581c0c8d --- /dev/null +++ b/plugins/hls-rename-plugin/test/testdata/QualifiedFunction.hs @@ -0,0 +1,4 @@ +import qualified Foo + +bar :: Int -> Int +bar = Foo.foo diff --git a/plugins/hls-rename-plugin/test/testdata/QualifiedShadowing.expected.hs b/plugins/hls-rename-plugin/test/testdata/QualifiedShadowing.expected.hs new file mode 100644 index 0000000000..52dbcea2b0 --- /dev/null +++ b/plugins/hls-rename-plugin/test/testdata/QualifiedShadowing.expected.hs @@ -0,0 +1,7 @@ +import qualified Foo as F + +bar :: Int -> Int +bar x = F.foobar x + foo x + +foo :: Int -> Int +foo _ = 5 diff --git a/plugins/hls-rename-plugin/test/testdata/QualifiedShadowing.hs b/plugins/hls-rename-plugin/test/testdata/QualifiedShadowing.hs new file mode 100644 index 0000000000..aa5e50caf6 --- /dev/null +++ b/plugins/hls-rename-plugin/test/testdata/QualifiedShadowing.hs @@ -0,0 +1,7 @@ +import qualified Foo as F + +bar :: Int -> Int +bar x = F.foo x + foo x + +foo :: Int -> Int +foo _ = 5 diff --git a/plugins/hls-rename-plugin/test/testdata/RecordField.expected.hs b/plugins/hls-rename-plugin/test/testdata/RecordField.expected.hs new file mode 100644 index 0000000000..6646df611c --- /dev/null +++ b/plugins/hls-rename-plugin/test/testdata/RecordField.expected.hs @@ -0,0 +1,7 @@ +data Bam = Bam { + number :: Int, + s :: String +} + +foo :: Bam -> Bam +foo Bam {number = y} = Bam {number = y + 5, s = ""} diff --git a/plugins/hls-rename-plugin/test/testdata/RecordField.hs b/plugins/hls-rename-plugin/test/testdata/RecordField.hs new file mode 100644 index 0000000000..873150935d --- /dev/null +++ b/plugins/hls-rename-plugin/test/testdata/RecordField.hs @@ -0,0 +1,7 @@ +data Bam = Bam { + n :: Int, + s :: String +} + +foo :: Bam -> Bam +foo Bam {n = y} = Bam {n = y + 5, s = ""} diff --git a/plugins/hls-rename-plugin/test/testdata/ShadowedName.expected.hs b/plugins/hls-rename-plugin/test/testdata/ShadowedName.expected.hs new file mode 100644 index 0000000000..7c6391176a --- /dev/null +++ b/plugins/hls-rename-plugin/test/testdata/ShadowedName.expected.hs @@ -0,0 +1,4 @@ +baz :: Int -> Int +baz x = foo + 10 + where + foo = 20 diff --git a/plugins/hls-rename-plugin/test/testdata/ShadowedName.hs b/plugins/hls-rename-plugin/test/testdata/ShadowedName.hs new file mode 100644 index 0000000000..513f8fa89f --- /dev/null +++ b/plugins/hls-rename-plugin/test/testdata/ShadowedName.hs @@ -0,0 +1,4 @@ +foo :: Int -> Int +foo x = foo + 10 + where + foo = 20 diff --git a/plugins/hls-rename-plugin/test/testdata/TypeConstructor.expected.hs b/plugins/hls-rename-plugin/test/testdata/TypeConstructor.expected.hs new file mode 100644 index 0000000000..0c46ffa077 --- /dev/null +++ b/plugins/hls-rename-plugin/test/testdata/TypeConstructor.expected.hs @@ -0,0 +1,5 @@ +data BinaryTree a = Node a (BinaryTree a) (BinaryTree a) | Leaf a + +rotateRight :: BinaryTree a -> BinaryTree a +rotateRight (Node v (Node v' l' r') r) = Node v' l' (Node v r' r) +rotateRight t = t diff --git a/plugins/hls-rename-plugin/test/testdata/TypeConstructor.hs b/plugins/hls-rename-plugin/test/testdata/TypeConstructor.hs new file mode 100644 index 0000000000..4e728aa1a4 --- /dev/null +++ b/plugins/hls-rename-plugin/test/testdata/TypeConstructor.hs @@ -0,0 +1,5 @@ +data Tree a = Node a (Tree a) (Tree a) | Leaf a + +rotateRight :: Tree a -> Tree a +rotateRight (Node v (Node v' l' r') r) = Node v' l' (Node v r' r) +rotateRight t = t diff --git a/plugins/hls-rename-plugin/test/testdata/TypeVariable.expected.hs b/plugins/hls-rename-plugin/test/testdata/TypeVariable.expected.hs new file mode 100644 index 0000000000..75891f4290 --- /dev/null +++ b/plugins/hls-rename-plugin/test/testdata/TypeVariable.expected.hs @@ -0,0 +1,2 @@ +bar :: Maybe b -> Maybe b +bar a = a diff --git a/plugins/hls-rename-plugin/test/testdata/TypeVariable.hs b/plugins/hls-rename-plugin/test/testdata/TypeVariable.hs new file mode 100644 index 0000000000..782be9a7f3 --- /dev/null +++ b/plugins/hls-rename-plugin/test/testdata/TypeVariable.hs @@ -0,0 +1,2 @@ +bar :: Maybe a -> Maybe a +bar a = a diff --git a/plugins/hls-rename-plugin/test/testdata/hie.yaml b/plugins/hls-rename-plugin/test/testdata/hie.yaml new file mode 100644 index 0000000000..6456069e16 --- /dev/null +++ b/plugins/hls-rename-plugin/test/testdata/hie.yaml @@ -0,0 +1,20 @@ +cradle: + direct: + arguments: + - "DataConstructor" + - "ExportedFunction" + - "Foo" + - "FunctionArgument" + - "FunctionName" + - "Gadt" + - "HiddenFunction" + - "ImportedFunction" + - "ImportHiding" + - "Indentation" + - "QualifiedAs" + - "QualifiedFunction" + - "QualifiedShadowing" + - "RecordField" + - "ShadowedName" + - "TypeConstructor" + - "TypeVariable" diff --git a/plugins/hls-retrie-plugin/src/Ide/Plugin/Retrie.hs b/plugins/hls-retrie-plugin/src/Ide/Plugin/Retrie.hs index f1c3487138..cf41005407 100644 --- a/plugins/hls-retrie-plugin/src/Ide/Plugin/Retrie.hs +++ b/plugins/hls-retrie-plugin/src/Ide/Plugin/Retrie.hs @@ -14,7 +14,7 @@ {-# OPTIONS -Wno-orphans #-} -module Ide.Plugin.Retrie (descriptor) where +module Ide.Plugin.Retrie (descriptor, response, handleMaybe, handleMaybeM) where import Control.Concurrent.Extra (readVar) import Control.Exception.Safe (Exception (..), diff --git a/stack-8.10.2.yaml b/stack-8.10.2.yaml index 893a340c60..8736053f3c 100644 --- a/stack-8.10.2.yaml +++ b/stack-8.10.2.yaml @@ -15,6 +15,7 @@ packages: - ./plugins/hls-explicit-imports-plugin - ./plugins/hls-refine-imports-plugin - ./plugins/hls-hlint-plugin + - ./plugins/hls-rename-plugin - ./plugins/hls-retrie-plugin - ./plugins/hls-splice-plugin - ./plugins/hls-tactics-plugin @@ -51,7 +52,7 @@ extra-deps: - implicit-hie-0.1.2.6 - monad-dijkstra-0.1.1.2 - refinery-0.4.0.0 - - retrie-0.1.1.1 + - retrie-1.0.0.0 - shake-0.19.4 - stylish-haskell-0.12.2.0 - semigroups-0.18.5 diff --git a/stack-8.10.3.yaml b/stack-8.10.3.yaml index c0d360d3f6..59d9672173 100644 --- a/stack-8.10.3.yaml +++ b/stack-8.10.3.yaml @@ -15,6 +15,7 @@ packages: - ./plugins/hls-explicit-imports-plugin - ./plugins/hls-refine-imports-plugin - ./plugins/hls-hlint-plugin + - ./plugins/hls-rename-plugin - ./plugins/hls-retrie-plugin - ./plugins/hls-splice-plugin - ./plugins/hls-tactics-plugin @@ -51,7 +52,7 @@ extra-deps: - implicit-hie-0.1.2.6 - monad-dijkstra-0.1.1.2 - refinery-0.4.0.0 - - retrie-0.1.1.1 + - retrie-1.0.0.0 - shake-0.19.4 - stylish-haskell-0.12.2.0 - semigroups-0.18.5 diff --git a/stack-8.10.4.yaml b/stack-8.10.4.yaml index b4c06fc578..a88d6bbfdb 100644 --- a/stack-8.10.4.yaml +++ b/stack-8.10.4.yaml @@ -15,6 +15,7 @@ packages: - ./plugins/hls-explicit-imports-plugin - ./plugins/hls-refine-imports-plugin - ./plugins/hls-hlint-plugin + - ./plugins/hls-rename-plugin - ./plugins/hls-retrie-plugin - ./plugins/hls-splice-plugin - ./plugins/hls-tactics-plugin @@ -48,7 +49,7 @@ extra-deps: - implicit-hie-0.1.2.6 - monad-dijkstra-0.1.1.2 - refinery-0.4.0.0 - - retrie-0.1.1.1 + - retrie-1.0.0.0 - stylish-haskell-0.12.2.0 - semigroups-0.18.5 - temporary-1.2.1.1 diff --git a/stack-8.10.5.yaml b/stack-8.10.5.yaml index 1e9b4281a3..168e2dcb7c 100644 --- a/stack-8.10.5.yaml +++ b/stack-8.10.5.yaml @@ -17,6 +17,7 @@ packages: - ./plugins/hls-explicit-imports-plugin - ./plugins/hls-refine-imports-plugin - ./plugins/hls-hlint-plugin + - ./plugins/hls-rename-plugin - ./plugins/hls-retrie-plugin - ./plugins/hls-splice-plugin - ./plugins/hls-tactics-plugin diff --git a/stack-8.8.3.yaml b/stack-8.8.3.yaml index c202d21c82..cb1ad7186a 100644 --- a/stack-8.8.3.yaml +++ b/stack-8.8.3.yaml @@ -15,6 +15,7 @@ packages: - ./plugins/hls-explicit-imports-plugin - ./plugins/hls-refine-imports-plugin - ./plugins/hls-hlint-plugin + - ./plugins/hls-rename-plugin - ./plugins/hls-retrie-plugin - ./plugins/hls-splice-plugin - ./plugins/hls-tactics-plugin @@ -64,7 +65,7 @@ extra-deps: - opentelemetry-extra-0.6.1 - ormolu-0.1.4.1 - refinery-0.4.0.0 - - retrie-0.1.1.1 + - retrie-1.0.0.0 - semigroups-0.18.5 - shake-0.19.4 - stylish-haskell-0.12.2.0 diff --git a/stack-8.8.4.yaml b/stack-8.8.4.yaml index 33c01989a6..a6fd3f147e 100644 --- a/stack-8.8.4.yaml +++ b/stack-8.8.4.yaml @@ -15,6 +15,7 @@ packages: - ./plugins/hls-explicit-imports-plugin - ./plugins/hls-refine-imports-plugin - ./plugins/hls-hlint-plugin + - ./plugins/hls-rename-plugin - ./plugins/hls-retrie-plugin - ./plugins/hls-splice-plugin - ./plugins/hls-tactics-plugin @@ -63,7 +64,7 @@ extra-deps: - opentelemetry-0.6.1 - opentelemetry-extra-0.6.1 - refinery-0.4.0.0 - - retrie-0.1.1.1 + - retrie-1.0.0.0 - semigroups-0.18.5 - shake-0.19.4 - stylish-haskell-0.12.2.0 diff --git a/stack-9.0.1.yaml b/stack-9.0.1.yaml index 112dada930..6a9c143054 100644 --- a/stack-9.0.1.yaml +++ b/stack-9.0.1.yaml @@ -16,6 +16,7 @@ packages: - ./plugins/hls-explicit-imports-plugin # - ./plugins/hls-refine-imports-plugin - ./plugins/hls-hlint-plugin + - ./plugins/hls-rename-plugin - ./plugins/hls-retrie-plugin # - ./plugins/hls-splice-plugin # - ./plugins/hls-tactics-plugin @@ -109,6 +110,7 @@ flags: pedantic: true class: false splice: false + rename: false refineImports: false tactic: false # Dependencies fail diff --git a/stack.yaml b/stack.yaml index b9ded8b709..bbc23834c7 100644 --- a/stack.yaml +++ b/stack.yaml @@ -15,6 +15,7 @@ packages: - ./plugins/hls-explicit-imports-plugin - ./plugins/hls-refine-imports-plugin - ./plugins/hls-hlint-plugin + - ./plugins/hls-rename-plugin - ./plugins/hls-retrie-plugin - ./plugins/hls-splice-plugin - ./plugins/hls-tactics-plugin @@ -45,7 +46,7 @@ extra-deps: - implicit-hie-0.1.2.6 - monad-dijkstra-0.1.1.2 - refinery-0.4.0.0 - - retrie-0.1.1.1 + - retrie-1.0.0.0 - stylish-haskell-0.12.2.0 - semigroups-0.18.5 - temporary-1.2.1.1 diff --git a/test/functional/Main.hs b/test/functional/Main.hs index 8da258b708..ff9473e56c 100644 --- a/test/functional/Main.hs +++ b/test/functional/Main.hs @@ -14,7 +14,6 @@ import HieBios import Highlight import Progress import Reference -import Rename import Symbol import Test.Hls import TypeDefinition @@ -37,7 +36,6 @@ main = defaultTestRunner , Highlight.tests , Progress.tests , Reference.tests - , Rename.tests , Symbol.tests , TypeDefinition.tests ] diff --git a/test/functional/Rename.hs b/test/functional/Rename.hs deleted file mode 100644 index 08de8a63d9..0000000000 --- a/test/functional/Rename.hs +++ /dev/null @@ -1,23 +0,0 @@ -{-# LANGUAGE OverloadedStrings #-} -module Rename (tests) where - -import Test.Hls -import Test.Hls.Command - -tests :: TestTree -tests = testGroup "rename" [ - ignoreTestBecause "no symbol renaming (yet!)" $ - testCase "works" $ - runSession hlsCommand fullCaps "test/testdata/rename" $ do - doc <- openDoc "Rename.hs" "haskell" - rename doc (Position 3 1) "baz" -- foo :: Int -> Int - contents <- documentContents doc - let expected = - "main = do\n\ - \ x <- return $ baz 42\n\ - \ return (baz x)\n\ - \baz :: Int -> Int\n\ - \baz x = x + 1\n\ - \bar = (+ 1) . baz\n" - liftIO $ contents @?= expected - ] diff --git a/test/utils/Test/Hls/Flags.hs b/test/utils/Test/Hls/Flags.hs index 84ff263f76..ce17c7568e 100644 --- a/test/utils/Test/Hls/Flags.hs +++ b/test/utils/Test/Hls/Flags.hs @@ -39,6 +39,14 @@ requiresImportLensPlugin = id requiresImportLensPlugin = ignoreTestBecause "ImportLens plugin disabled" #endif +-- | Disable test unless the rename flag is set +requiresRenamePlugin :: TestTree -> TestTree +#if rename +requiresRenamePlugin = id +#else +requiresRenamePlugin = ignoreTestBecause "Rename plugin disabled" +#endif + -- | Disable test unless the retrie flag is set requiresRetriePlugin :: TestTree -> TestTree #if retrie From 3fd6b8aef3f86ece1fe3c76c6065a723bea2f08d Mon Sep 17 00:00:00 2001 From: Oliver Madine <30090176+OliverMadine@users.noreply.github.com> Date: Tue, 17 Aug 2021 03:40:38 +0100 Subject: [PATCH 02/13] code style --- .../hls-rename-plugin/hls-rename-plugin.cabal | 20 +++++----- .../src/Ide/Plugin/Rename.hs | 1 + plugins/hls-rename-plugin/test/Main.hs | 37 +++++++++---------- .../hls-rename-plugin/test/testdata/hie.yaml | 2 +- 4 files changed, 29 insertions(+), 31 deletions(-) diff --git a/plugins/hls-rename-plugin/hls-rename-plugin.cabal b/plugins/hls-rename-plugin/hls-rename-plugin.cabal index 87a1ced1b7..ea9b62e2a2 100644 --- a/plugins/hls-rename-plugin/hls-rename-plugin.cabal +++ b/plugins/hls-rename-plugin/hls-rename-plugin.cabal @@ -22,20 +22,20 @@ library build-depends: , aeson , base >=4.12 && <5 - , lsp-types - , lsp - , hls-retrie-plugin - , hls-plugin-api ^>=1.2 - , retrie >=0.1.1.1 - , ghcide >=1.4 && <1.5 - , transformers - , text - , unordered-containers , containers , ghc - , hiedb , ghc-exactprint + , ghcide >=1.4 && <1.5 + , hiedb + , hls-plugin-api ^>=1.2 + , hls-retrie-plugin >=1.0.1.1 + , lsp + , lsp-types + , retrie >=0.1.1.1 , syb + , text + , transformers + , unordered-containers default-language: Haskell2010 diff --git a/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs b/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs index 59ed0722a9..d941b4d352 100644 --- a/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs +++ b/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs @@ -3,6 +3,7 @@ {-# LANGUAGE NamedFieldPuns #-} module Ide.Plugin.Rename (descriptor) where + import Control.Monad import Control.Monad.IO.Class (MonadIO (liftIO)) import Control.Monad.Trans.Except diff --git a/plugins/hls-rename-plugin/test/Main.hs b/plugins/hls-rename-plugin/test/Main.hs index c562b376a3..01c51be3eb 100644 --- a/plugins/hls-rename-plugin/test/Main.hs +++ b/plugins/hls-rename-plugin/test/Main.hs @@ -15,32 +15,32 @@ renamePlugin = Rename.descriptor "rename" tests :: TestTree tests = testGroup "rename" [ testGroup "Top-level renames" - [ goldenWithRename "function name" "FunctionName" $ \doc -> do + [ goldenWithRename "data constructor" "DataConstructor" $ \doc -> do + rename doc (Position 0 15) "Op" + , goldenWithRename "exported function" "ExportedFunction" $ \doc -> do + rename doc (Position 2 1) "quux" + , goldenWithRename "function name" "FunctionName" $ \doc -> do rename doc (Position 3 1) "baz" , goldenWithRename "GADT" "Gadt" $ \doc -> do rename doc (Position 6 37) "Expr" - , goldenWithRename "imported function" "ImportedFunction" $ \doc -> do - rename doc (Position 3 8) "baz" - , goldenWithRename "exported function" "ExportedFunction" $ \doc -> do - rename doc (Position 2 1) "quux" , goldenWithRename "hidden function" "HiddenFunction" $ \doc -> do rename doc (Position 0 32) "quux" - , goldenWithRename "allign indentation" "Indentation" $ \doc -> do - rename doc (Position 0 2) "fooBarQuux" - , goldenWithRename "shadowed name" "ShadowedName" $ \doc -> do - rename doc (Position 1 1) "baz" - , goldenWithRename "qualified function" "QualifiedFunction" $ \doc -> do - rename doc (Position 3 12) "baz" - , goldenWithRename "type constructor" "TypeConstructor" $ \doc -> do - rename doc (Position 2 17) "BinaryTree" - , goldenWithRename "data constructor" "DataConstructor" $ \doc -> do - rename doc (Position 0 15) "Op" + , goldenWithRename "imported function" "ImportedFunction" $ \doc -> do + rename doc (Position 3 8) "baz" , goldenWithRename "import hiding" "ImportHiding" $ \doc -> do rename doc (Position 0 22) "hiddenFoo" + , goldenWithRename "allign indentation" "Indentation" $ \doc -> do + rename doc (Position 0 2) "fooBarQuux" , goldenWithRename "qualified as" "QualifiedAs" $ \doc -> do rename doc (Position 3 10) "baz" , goldenWithRename "qualified shadowing" "QualifiedShadowing" $ \doc -> do rename doc (Position 3 12) "foobar" + , goldenWithRename "qualified function" "QualifiedFunction" $ \doc -> do + rename doc (Position 3 12) "baz" + , goldenWithRename "shadowed name" "ShadowedName" $ \doc -> do + rename doc (Position 1 1) "baz" + , goldenWithRename "type constructor" "TypeConstructor" $ \doc -> do + rename doc (Position 2 17) "BinaryTree" ] , testGroup "non Top-level renames" [ goldenWithRename "function argument" "FunctionArgument" $ \doc -> do @@ -53,11 +53,8 @@ tests = testGroup "rename" ] goldenWithRename :: TestName -> FilePath -> (TextDocumentIdentifier -> Session ()) -> TestTree -goldenWithRename title path act = - goldenWithHaskellDoc renamePlugin title testDataDir path "expected" "hs" $ \doc -> do - waitForProgressDone - waitForProgressDone - act doc +goldenWithRename title path = + goldenWithHaskellDoc renamePlugin title testDataDir path "expected" "hs" testDataDir :: FilePath testDataDir = "test" "testdata" diff --git a/plugins/hls-rename-plugin/test/testdata/hie.yaml b/plugins/hls-rename-plugin/test/testdata/hie.yaml index 6456069e16..535eb08fd1 100644 --- a/plugins/hls-rename-plugin/test/testdata/hie.yaml +++ b/plugins/hls-rename-plugin/test/testdata/hie.yaml @@ -8,8 +8,8 @@ cradle: - "FunctionName" - "Gadt" - "HiddenFunction" - - "ImportedFunction" - "ImportHiding" + - "ImportedFunction" - "Indentation" - "QualifiedAs" - "QualifiedFunction" From eb329be5b7013eb8629c6e91e559ab8b559d4db7 Mon Sep 17 00:00:00 2001 From: Oliver Madine <30090176+OliverMadine@users.noreply.github.com> Date: Tue, 17 Aug 2021 03:55:41 +0100 Subject: [PATCH 03/13] enable for ghc < 8.8.0 --- .github/workflows/test.yml | 2 +- haskell-language-server.cabal | 4 ++-- plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs | 2 +- stack-8.6.4.yaml | 1 + stack-8.6.5.yaml | 1 + 5 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index dd8f575818..6585ec8448 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -209,6 +209,6 @@ jobs: name: Test hls-call-hierarchy-plugin test suite run: cabal test hls-call-hierarchy-plugin --test-options="-j1 --rerun-update" || cabal test hls-call-hierarchy-plugin --test-options="-j1 --rerun" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-call-hierarchy-plugin --test-options="-j1 --rerun" - - if: ${{ needs.pre_job.outputs.should_skip != 'true' && matrix.test && matrix.ghc >= '8.8.0'}} + - if: ${{ needs.pre_job.outputs.should_skip != 'true' && matrix.test}} name: Test hls-rename-plugin test suite run: cabal test hls-rename-plugin --test-options="-j1 --rerun-update" || cabal test hls-rename-plugin --test-options="-j1 --rerun" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-call-hierarchy-plugin --rename-options="-j1 --rerun" diff --git a/haskell-language-server.cabal b/haskell-language-server.cabal index 3f8f6095a4..e25441ba5a 100644 --- a/haskell-language-server.cabal +++ b/haskell-language-server.cabal @@ -229,7 +229,7 @@ common refineImports cpp-options: -DrefineImports common rename - if impl(ghc >= 8.8) && (flag(rename) || flag(all-plugins)) + if flag(rename) || flag(all-plugins) build-depends: hls-rename-plugin ^>= 1.0.0.0 cpp-options: -Drename @@ -457,7 +457,7 @@ test-suite func-test cpp-options: -Deval if flag(importLens) || flag(all-plugins) cpp-options: -DimportLens - if impl(ghc > 8.8) && (flag(rename) || flag(all-plugins)) + if flag(rename) || flag(all-plugins) cpp-options: -Drename if flag(retrie) || flag(all-plugins) cpp-options: -Dretrie diff --git a/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs b/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs index d941b4d352..736b235c70 100644 --- a/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs +++ b/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs @@ -3,7 +3,7 @@ {-# LANGUAGE NamedFieldPuns #-} module Ide.Plugin.Rename (descriptor) where - + import Control.Monad import Control.Monad.IO.Class (MonadIO (liftIO)) import Control.Monad.Trans.Except diff --git a/stack-8.6.4.yaml b/stack-8.6.4.yaml index b37a0503b6..ad17bae96c 100644 --- a/stack-8.6.4.yaml +++ b/stack-8.6.4.yaml @@ -16,6 +16,7 @@ packages: - ./plugins/hls-explicit-imports-plugin - ./plugins/hls-refine-imports-plugin - ./plugins/hls-hlint-plugin + - ./plugins/hls-rename-plugin - ./plugins/hls-retrie-plugin - ./plugins/hls-splice-plugin - ./plugins/hls-tactics-plugin diff --git a/stack-8.6.5.yaml b/stack-8.6.5.yaml index f012979944..0cce8fe49e 100644 --- a/stack-8.6.5.yaml +++ b/stack-8.6.5.yaml @@ -15,6 +15,7 @@ packages: - ./plugins/hls-explicit-imports-plugin - ./plugins/hls-refine-imports-plugin - ./plugins/hls-hlint-plugin + - ./plugins/hls-rename-plugin - ./plugins/hls-retrie-plugin - ./plugins/hls-splice-plugin - ./plugins/hls-tactics-plugin From 78474c61c78d713192b981fa7256832ab66daed4 Mon Sep 17 00:00:00 2001 From: Oliver Madine <30090176+OliverMadine@users.noreply.github.com> Date: Tue, 17 Aug 2021 16:52:47 +0100 Subject: [PATCH 04/13] remove aeson from build-depends --- plugins/hls-rename-plugin/hls-rename-plugin.cabal | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/hls-rename-plugin/hls-rename-plugin.cabal b/plugins/hls-rename-plugin/hls-rename-plugin.cabal index ea9b62e2a2..89c9187c54 100644 --- a/plugins/hls-rename-plugin/hls-rename-plugin.cabal +++ b/plugins/hls-rename-plugin/hls-rename-plugin.cabal @@ -20,7 +20,6 @@ library exposed-modules: Ide.Plugin.Rename hs-source-dirs: src build-depends: - , aeson , base >=4.12 && <5 , containers , ghc From 09dfa3b74158d7bb412bf86c3a5c05eab5653ebc Mon Sep 17 00:00:00 2001 From: Oliver Madine <30090176+OliverMadine@users.noreply.github.com> Date: Tue, 17 Aug 2021 17:49:08 +0100 Subject: [PATCH 05/13] update support support ghc9 revert retrie version bump disable feature by default --- cabal-ghc901.project | 2 +- configuration-ghc-901.nix | 2 -- hls-plugin-api/src/Ide/Plugin/Config.hs | 2 +- stack-8.10.2.yaml | 2 +- stack-8.10.3.yaml | 2 +- stack-8.10.4.yaml | 2 +- stack-8.10.5.yaml | 2 +- stack-8.8.3.yaml | 2 +- stack-8.8.4.yaml | 2 +- stack-9.0.1.yaml | 3 +-- stack.yaml | 2 +- 11 files changed, 10 insertions(+), 13 deletions(-) diff --git a/cabal-ghc901.project b/cabal-ghc901.project index aa84008ecb..1396db52b6 100644 --- a/cabal-ghc901.project +++ b/cabal-ghc901.project @@ -80,7 +80,7 @@ index-state: 2021-08-08T02:21:16Z constraints: -- These plugins doesn't work on GHC9 yet - haskell-language-server -brittany -class -fourmolu -splice -stylishhaskell -tactic -refineImports -rename + haskell-language-server -brittany -class -fourmolu -splice -stylishhaskell -tactic -refineImports allow-newer: diff --git a/configuration-ghc-901.nix b/configuration-ghc-901.nix index aca5b1a5b1..7f3d38554c 100644 --- a/configuration-ghc-901.nix +++ b/configuration-ghc-901.nix @@ -7,7 +7,6 @@ let "hls-brittany-plugin" "hls-stylish-haskell-plugin" "hls-fourmolu-plugin" - "hls-rename-plugin" "hls-splice-plugin" "hls-class-plugin" "hls-refine-imports-plugin" @@ -92,7 +91,6 @@ let "-f-brittany" "-f-class" "-f-fourmolu" - "-f-rename" "-f-splice" "-f-stylishhaskell" "-f-tactic" diff --git a/hls-plugin-api/src/Ide/Plugin/Config.hs b/hls-plugin-api/src/Ide/Plugin/Config.hs index 74320887ea..d5985d3e86 100644 --- a/hls-plugin-api/src/Ide/Plugin/Config.hs +++ b/hls-plugin-api/src/Ide/Plugin/Config.hs @@ -143,7 +143,7 @@ instance Default PluginConfig where , plcHoverOn = True , plcSymbolsOn = True , plcCompletionOn = True - , plcRenameOn = True + , plcRenameOn = False , plcConfig = mempty } diff --git a/stack-8.10.2.yaml b/stack-8.10.2.yaml index 8736053f3c..d752f42991 100644 --- a/stack-8.10.2.yaml +++ b/stack-8.10.2.yaml @@ -52,7 +52,7 @@ extra-deps: - implicit-hie-0.1.2.6 - monad-dijkstra-0.1.1.2 - refinery-0.4.0.0 - - retrie-1.0.0.0 + - retrie-0.1.1.1 - shake-0.19.4 - stylish-haskell-0.12.2.0 - semigroups-0.18.5 diff --git a/stack-8.10.3.yaml b/stack-8.10.3.yaml index 59d9672173..4da46734bc 100644 --- a/stack-8.10.3.yaml +++ b/stack-8.10.3.yaml @@ -52,7 +52,7 @@ extra-deps: - implicit-hie-0.1.2.6 - monad-dijkstra-0.1.1.2 - refinery-0.4.0.0 - - retrie-1.0.0.0 + - retrie-0.1.1.1 - shake-0.19.4 - stylish-haskell-0.12.2.0 - semigroups-0.18.5 diff --git a/stack-8.10.4.yaml b/stack-8.10.4.yaml index a88d6bbfdb..2c5fb2df71 100644 --- a/stack-8.10.4.yaml +++ b/stack-8.10.4.yaml @@ -49,7 +49,7 @@ extra-deps: - implicit-hie-0.1.2.6 - monad-dijkstra-0.1.1.2 - refinery-0.4.0.0 - - retrie-1.0.0.0 + - retrie-0.1.1.1 - stylish-haskell-0.12.2.0 - semigroups-0.18.5 - temporary-1.2.1.1 diff --git a/stack-8.10.5.yaml b/stack-8.10.5.yaml index 168e2dcb7c..e3c18f263d 100644 --- a/stack-8.10.5.yaml +++ b/stack-8.10.5.yaml @@ -51,7 +51,7 @@ extra-deps: - implicit-hie-0.1.2.6 - monad-dijkstra-0.1.1.2 - refinery-0.4.0.0 - - retrie-1.0.0.0 + - retrie-0.1.1.1 - stylish-haskell-0.12.2.0 - semigroups-0.18.5 - temporary-1.2.1.1 diff --git a/stack-8.8.3.yaml b/stack-8.8.3.yaml index cb1ad7186a..1da0a06e4f 100644 --- a/stack-8.8.3.yaml +++ b/stack-8.8.3.yaml @@ -65,7 +65,7 @@ extra-deps: - opentelemetry-extra-0.6.1 - ormolu-0.1.4.1 - refinery-0.4.0.0 - - retrie-1.0.0.0 + - retrie-0.1.1.1 - semigroups-0.18.5 - shake-0.19.4 - stylish-haskell-0.12.2.0 diff --git a/stack-8.8.4.yaml b/stack-8.8.4.yaml index a6fd3f147e..1b524876c7 100644 --- a/stack-8.8.4.yaml +++ b/stack-8.8.4.yaml @@ -64,7 +64,7 @@ extra-deps: - opentelemetry-0.6.1 - opentelemetry-extra-0.6.1 - refinery-0.4.0.0 - - retrie-1.0.0.0 + - retrie-0.1.1.1 - semigroups-0.18.5 - shake-0.19.4 - stylish-haskell-0.12.2.0 diff --git a/stack-9.0.1.yaml b/stack-9.0.1.yaml index 6a9c143054..7dba0a6fa6 100644 --- a/stack-9.0.1.yaml +++ b/stack-9.0.1.yaml @@ -53,7 +53,7 @@ extra-deps: - lens-5.0.1 - profunctors-5.6.2 - refinery-0.4.0.0 -- retrie-1.0.0.0 +- retrie-0.1.1.1 - some-1.0.2@sha256:3d460998df32ad7b93bf55657aeae988d97070155e71718b4bc75d0997ce9d62,2244 - lsp-1.2.0.1 - lsp-types-1.3.0.0 @@ -110,7 +110,6 @@ flags: pedantic: true class: false splice: false - rename: false refineImports: false tactic: false # Dependencies fail diff --git a/stack.yaml b/stack.yaml index bbc23834c7..4feb84d556 100644 --- a/stack.yaml +++ b/stack.yaml @@ -46,7 +46,7 @@ extra-deps: - implicit-hie-0.1.2.6 - monad-dijkstra-0.1.1.2 - refinery-0.4.0.0 - - retrie-1.0.0.0 + - retrie-0.1.1.1 - stylish-haskell-0.12.2.0 - semigroups-0.18.5 - temporary-1.2.1.1 From 108d637c82d1fe08e482761c50020f6ea588529c Mon Sep 17 00:00:00 2001 From: Oliver Madine <30090176+OliverMadine@users.noreply.github.com> Date: Tue, 17 Aug 2021 18:25:18 +0100 Subject: [PATCH 06/13] enable plugin for testing --- .github/workflows/test.yml | 1 - haskell-language-server.cabal | 2 +- hls-plugin-api/src/Ide/Plugin/Config.hs | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6585ec8448..1b4ae13d8e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -211,4 +211,3 @@ jobs: - if: ${{ needs.pre_job.outputs.should_skip != 'true' && matrix.test}} name: Test hls-rename-plugin test suite - run: cabal test hls-rename-plugin --test-options="-j1 --rerun-update" || cabal test hls-rename-plugin --test-options="-j1 --rerun" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-call-hierarchy-plugin --rename-options="-j1 --rerun" diff --git a/haskell-language-server.cabal b/haskell-language-server.cabal index e25441ba5a..0cf7d7d30e 100644 --- a/haskell-language-server.cabal +++ b/haskell-language-server.cabal @@ -133,7 +133,7 @@ flag refineImports flag rename description: Enable rename plugin - default: True + default: False manual: True flag retrie diff --git a/hls-plugin-api/src/Ide/Plugin/Config.hs b/hls-plugin-api/src/Ide/Plugin/Config.hs index d5985d3e86..74320887ea 100644 --- a/hls-plugin-api/src/Ide/Plugin/Config.hs +++ b/hls-plugin-api/src/Ide/Plugin/Config.hs @@ -143,7 +143,7 @@ instance Default PluginConfig where , plcHoverOn = True , plcSymbolsOn = True , plcCompletionOn = True - , plcRenameOn = False + , plcRenameOn = True , plcConfig = mempty } From 9cc18eee024c699251841d1665c765dab851b03a Mon Sep 17 00:00:00 2001 From: Oliver Madine <30090176+OliverMadine@users.noreply.github.com> Date: Tue, 17 Aug 2021 18:28:49 +0100 Subject: [PATCH 07/13] fix test.yml --- .github/workflows/test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1b4ae13d8e..4da0c9060b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -211,3 +211,4 @@ jobs: - if: ${{ needs.pre_job.outputs.should_skip != 'true' && matrix.test}} name: Test hls-rename-plugin test suite + run: cabal test hls-rename-plugin --flag"+rename" --test-options="-j1 --rerun-update" || cabal test hls-rename-plugin --test-options="-j1 --rerun" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-rename-plugin --test-options="-j1 --rerun" From bcbde30255e3b92d5b21f608c9fa61f2057e6d39 Mon Sep 17 00:00:00 2001 From: Oliver Madine <30090176+OliverMadine@users.noreply.github.com> Date: Tue, 17 Aug 2021 20:43:07 +0100 Subject: [PATCH 08/13] code style, ghc9 retrie bump remove unnecessary flag in test.yml consistent indentation increase retrie version for ghc 9.0.1 (decreased by mistake) --- .github/workflows/test.yml | 2 +- .../src/Ide/Plugin/Rename.hs | 57 +++++++++---------- stack-9.0.1.yaml | 2 +- 3 files changed, 29 insertions(+), 32 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4da0c9060b..4ad4d30000 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -211,4 +211,4 @@ jobs: - if: ${{ needs.pre_job.outputs.should_skip != 'true' && matrix.test}} name: Test hls-rename-plugin test suite - run: cabal test hls-rename-plugin --flag"+rename" --test-options="-j1 --rerun-update" || cabal test hls-rename-plugin --test-options="-j1 --rerun" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-rename-plugin --test-options="-j1 --rerun" + run: cabal test hls-rename-plugin --test-options="-j1 --rerun-update" || cabal test hls-rename-plugin --test-options="-j1 --rerun" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-rename-plugin --test-options="-j1 --rerun" diff --git a/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs b/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs index 736b235c70..9e0e88479c 100644 --- a/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs +++ b/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs @@ -1,5 +1,6 @@ {-# LANGUAGE CPP #-} {-# LANGUAGE DataKinds #-} +{-# LANGUAGE GADTs #-} {-# LANGUAGE NamedFieldPuns #-} module Ide.Plugin.Rename (descriptor) where @@ -28,8 +29,7 @@ import Ide.PluginUtils import Ide.Types import Language.LSP.Server import Language.LSP.Types -import Retrie hiding (HasSrcSpan, - HsModule, getLoc) +import Retrie hiding (HasSrcSpan, HsModule, getLoc) descriptor :: PluginId -> PluginDescriptor IdeState descriptor pluginId = (defaultPluginDescriptor pluginId) { @@ -37,19 +37,19 @@ descriptor pluginId = (defaultPluginDescriptor pluginId) { } renameProvider :: PluginMethodHandler IdeState TextDocumentRename -renameProvider state pluginId (RenameParams (TextDocumentIdentifier uri) pos _prog newNameText) = response $ do - nfp <- safeGetNfp uri - oldName <- (handleMaybe "error: could not find name at pos" . listToMaybe) =<< - getNamesAtPos state pos nfp - refs <- refsAtName state nfp oldName - refFiles <- mapM safeGetNfp (nub [uri | Location uri _ <- refs]) - let newNameStr = T.unpack newNameText - newOccName = mkTcOcc newNameStr +renameProvider state pluginId (RenameParams (TextDocumentIdentifier uri) pos _prog newNameText) = + response $ do + nfp <- safeGetNfp uri + oldName <- (handleMaybe "error: could not find name at pos" . listToMaybe) =<< + getNamesAtPos state pos nfp + refs <- refsAtName state nfp oldName + refFiles <- mapM safeGetNfp (nub [uri | Location uri _ <- refs]) - nfpEdits <- mapMToSnd (getSrcEdits state (renameRefs refs (mkTcOcc newNameStr))) refFiles - let uriEdits = HM.fromList $ map (Data.Bifunctor.first nfpToUri) nfpEdits + let newOccName = mkTcOcc $ T.unpack newNameText + nfpEdits <- mapMToSnd (getSrcEdits state (renameRefs refs newOccName)) refFiles + let uriEdits = HM.fromList $ map (Data.Bifunctor.first nfpToUri) nfpEdits - pure $ WorkspaceEdit (Just uriEdits) Nothing Nothing + pure $ WorkspaceEdit (Just uriEdits) Nothing Nothing ------------------------------------------------------------------------------- -- Source renaming @@ -77,29 +77,26 @@ renameRefs :: [Location] -> OccName #if MIN_VERSION_ghc(9,0,1) - -> HsModule - -> HsModule + -> (HsModule -> HsModule) #else - -> HsModule GhcPs - -> HsModule GhcPs + -> (HsModule GhcPs -> HsModule GhcPs) #endif renameRefs refs newOccName = everywhere $ mkT replace where replace :: Located RdrName -> Located RdrName - replace lOldRdrName - | isRef refs lOldRdrName = fmap (const newRdrName) lOldRdrName - | otherwise = lOldRdrName - where - newRdrName = - case unLoc lOldRdrName of - Qual modName _ -> Qual modName newOccName - _ -> Unqual newOccName + replace (L srcSpan oldRdrName) + | isRef srcSpan = L srcSpan $ newRdrName oldRdrName + replace lOldRdrName = lOldRdrName - isRef :: HasSrcSpan a => [Location] -> a -> Bool - isRef refs = (`elem` refs) . fromJust . srcSpanToLocation . getLoc + newRdrName srcSpan = case srcSpan of + Qual modName _ -> Qual modName newOccName + _ -> Unqual newOccName ------------------------------------------------------------- --- reference finding + isRef :: SrcSpan -> Bool + isRef = (`elem` refs) . fromJust . srcSpanToLocation + +------------------------------------------------------------------------------- +-- Reference finding refsAtName :: IdeState -> NormalizedFilePath -> Name -> ExceptT [Char] (LspT Config IO) [Location] refsAtName state nfp name = do @@ -121,7 +118,7 @@ getNameAstLocations name (HAR _ _ rm _ _, mapping) = mapMaybe (toCurrentLocation mapping . realSrcSpanToLocation . fst) <$> M.lookup (Right name) rm ------------------------------------------------------------------------------- --- util +-- Util safeGetNfp :: (Monad m) => Uri -> ExceptT String m NormalizedFilePath safeGetNfp uri = handleMaybe "error: uri" $ toNormalizedFilePath <$> uriToFilePath uri diff --git a/stack-9.0.1.yaml b/stack-9.0.1.yaml index 7dba0a6fa6..121d2ee515 100644 --- a/stack-9.0.1.yaml +++ b/stack-9.0.1.yaml @@ -53,7 +53,7 @@ extra-deps: - lens-5.0.1 - profunctors-5.6.2 - refinery-0.4.0.0 -- retrie-0.1.1.1 +- retrie-1.0.0.0 - some-1.0.2@sha256:3d460998df32ad7b93bf55657aeae988d97070155e71718b4bc75d0997ce9d62,2244 - lsp-1.2.0.1 - lsp-types-1.3.0.0 From 6c994a0fb22858124a4a2ce15237e24378c5f769 Mon Sep 17 00:00:00 2001 From: Oliver Madine <30090176+OliverMadine@users.noreply.github.com> Date: Tue, 17 Aug 2021 22:41:45 +0100 Subject: [PATCH 09/13] avoid using Data.List.nub --- plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs b/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs index 9e0e88479c..83903739b4 100644 --- a/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs +++ b/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs @@ -12,7 +12,7 @@ import qualified Data.Bifunctor import Data.Containers.ListUtils import Data.Generics import qualified Data.HashMap.Strict as HM -import Data.List +import qualified Data.HashSet as HashSet import qualified Data.Map as M import Data.Maybe import qualified Data.Text as T @@ -43,7 +43,7 @@ renameProvider state pluginId (RenameParams (TextDocumentIdentifier uri) pos _pr oldName <- (handleMaybe "error: could not find name at pos" . listToMaybe) =<< getNamesAtPos state pos nfp refs <- refsAtName state nfp oldName - refFiles <- mapM safeGetNfp (nub [uri | Location uri _ <- refs]) + refFiles <- mapM safeGetNfp (HashSet.toList $ HashSet.fromList [uri | Location uri _ <- refs]) let newOccName = mkTcOcc $ T.unpack newNameText nfpEdits <- mapMToSnd (getSrcEdits state (renameRefs refs newOccName)) refFiles From 28795968847b10d9d5d6308885b2c13973063344 Mon Sep 17 00:00:00 2001 From: Oliver Madine <30090176+OliverMadine@users.noreply.github.com> Date: Wed, 18 Aug 2021 07:41:10 +0100 Subject: [PATCH 10/13] code style fix typo in indentation test break lines at 100 chars rename some vars --- plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs | 13 +++++++++---- plugins/hls-rename-plugin/test/Main.hs | 2 +- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs b/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs index 83903739b4..03e581545a 100644 --- a/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs +++ b/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs @@ -12,7 +12,7 @@ import qualified Data.Bifunctor import Data.Containers.ListUtils import Data.Generics import qualified Data.HashMap.Strict as HM -import qualified Data.HashSet as HashSet +import qualified Data.HashSet as HS import qualified Data.Map as M import Data.Maybe import qualified Data.Text as T @@ -43,7 +43,7 @@ renameProvider state pluginId (RenameParams (TextDocumentIdentifier uri) pos _pr oldName <- (handleMaybe "error: could not find name at pos" . listToMaybe) =<< getNamesAtPos state pos nfp refs <- refsAtName state nfp oldName - refFiles <- mapM safeGetNfp (HashSet.toList $ HashSet.fromList [uri | Location uri _ <- refs]) + refFiles <- mapM safeGetNfp $ HS.toList $ HS.fromList [uri | Location uri _ <- refs] let newOccName = mkTcOcc $ T.unpack newNameText nfpEdits <- mapMToSnd (getSrcEdits state (renameRefs refs newOccName)) refFiles @@ -88,7 +88,8 @@ renameRefs refs newOccName = everywhere $ mkT replace | isRef srcSpan = L srcSpan $ newRdrName oldRdrName replace lOldRdrName = lOldRdrName - newRdrName srcSpan = case srcSpan of + newRdrName :: RdrName -> RdrName + newRdrName oldRdrName = case oldRdrName of Qual modName _ -> Qual modName newOccName _ -> Unqual newOccName @@ -123,7 +124,11 @@ getNameAstLocations name (HAR _ _ rm _ _, mapping) = safeGetNfp :: (Monad m) => Uri -> ExceptT String m NormalizedFilePath safeGetNfp uri = handleMaybe "error: uri" $ toNormalizedFilePath <$> uriToFilePath uri -getNamesAtPos :: IdeState -> Position -> NormalizedFilePath -> ExceptT String (LspT Config IO) [Name] +getNamesAtPos :: + IdeState + -> Position + -> NormalizedFilePath + -> ExceptT String (LspT Config IO) [Name] getNamesAtPos state pos nfp = do (HAR{hieAst}, mapping) <- handleMaybeM "error: ast" $ liftIO $ runAction "Rename.GetHieAst" state $ useWithStale GetHieAst nfp diff --git a/plugins/hls-rename-plugin/test/Main.hs b/plugins/hls-rename-plugin/test/Main.hs index 01c51be3eb..1425db0261 100644 --- a/plugins/hls-rename-plugin/test/Main.hs +++ b/plugins/hls-rename-plugin/test/Main.hs @@ -29,7 +29,7 @@ tests = testGroup "rename" rename doc (Position 3 8) "baz" , goldenWithRename "import hiding" "ImportHiding" $ \doc -> do rename doc (Position 0 22) "hiddenFoo" - , goldenWithRename "allign indentation" "Indentation" $ \doc -> do + , goldenWithRename "realign indentation" "Indentation" $ \doc -> do rename doc (Position 0 2) "fooBarQuux" , goldenWithRename "qualified as" "QualifiedAs" $ \doc -> do rename doc (Position 3 10) "baz" From e3ddd94695610aff8d5c79e66a9fd020f8a047f6 Mon Sep 17 00:00:00 2001 From: Oliver Madine <30090176+OliverMadine@users.noreply.github.com> Date: Thu, 19 Aug 2021 07:59:29 +0100 Subject: [PATCH 11/13] code style / performance remove Retire dependency by using exactprint directly group references by file to increase speed of checking `elem` per file add test cases (typeclass, let expression) add some documentation fix bug when looking up reference in HieDb improve error messages rename some function / vars --- .../hls-rename-plugin/hls-rename-plugin.cabal | 3 +- .../src/Ide/Plugin/Rename.hs | 139 ++++++++++-------- plugins/hls-rename-plugin/test/Main.hs | 74 +++++----- .../test/testdata/FunctionName.expected.hs | 2 +- .../test/testdata/FunctionName.hs | 2 +- .../test/testdata/LetExpression.expected.hs | 10 ++ .../test/testdata/LetExpression.hs | 10 ++ ...tion.expected.hs => RealignDo.expected.hs} | 0 .../testdata/{Indentation.hs => RealignDo.hs} | 0 .../test/testdata/Typeclass.expected.hs | 10 ++ .../test/testdata/Typeclass.hs | 10 ++ .../hls-rename-plugin/test/testdata/hie.yaml | 4 +- 12 files changed, 160 insertions(+), 104 deletions(-) create mode 100644 plugins/hls-rename-plugin/test/testdata/LetExpression.expected.hs create mode 100644 plugins/hls-rename-plugin/test/testdata/LetExpression.hs rename plugins/hls-rename-plugin/test/testdata/{Indentation.expected.hs => RealignDo.expected.hs} (100%) rename plugins/hls-rename-plugin/test/testdata/{Indentation.hs => RealignDo.hs} (100%) create mode 100644 plugins/hls-rename-plugin/test/testdata/Typeclass.expected.hs create mode 100644 plugins/hls-rename-plugin/test/testdata/Typeclass.hs diff --git a/plugins/hls-rename-plugin/hls-rename-plugin.cabal b/plugins/hls-rename-plugin/hls-rename-plugin.cabal index 89c9187c54..1d1499d44f 100644 --- a/plugins/hls-rename-plugin/hls-rename-plugin.cabal +++ b/plugins/hls-rename-plugin/hls-rename-plugin.cabal @@ -22,6 +22,7 @@ library build-depends: , base >=4.12 && <5 , containers + , extra , ghc , ghc-exactprint , ghcide >=1.4 && <1.5 @@ -30,11 +31,9 @@ library , hls-retrie-plugin >=1.0.1.1 , lsp , lsp-types - , retrie >=0.1.1.1 , syb , text , transformers - , unordered-containers default-language: Haskell2010 diff --git a/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs b/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs index 03e581545a..7e02739b8a 100644 --- a/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs +++ b/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs @@ -7,12 +7,11 @@ module Ide.Plugin.Rename (descriptor) where import Control.Monad import Control.Monad.IO.Class (MonadIO (liftIO)) +import Control.Monad.Trans.Class import Control.Monad.Trans.Except -import qualified Data.Bifunctor import Data.Containers.ListUtils import Data.Generics -import qualified Data.HashMap.Strict as HM -import qualified Data.HashSet as HS +import Data.List.Extra hiding (nubOrd) import qualified Data.Map as M import Data.Maybe import qualified Data.Text as T @@ -20,16 +19,16 @@ import Development.IDE hiding (pluginHandlers) import Development.IDE.Core.PositionMapping import Development.IDE.Core.Shake import Development.IDE.GHC.Compat -import Development.IDE.GHC.ExactPrint import Development.IDE.Spans.AtPoint +import GhcPlugins hiding ((<>)) import HieDb.Query import Ide.Plugin.Config import Ide.Plugin.Retrie hiding (descriptor) import Ide.PluginUtils import Ide.Types +import Language.Haskell.GHC.ExactPrint import Language.LSP.Server import Language.LSP.Types -import Retrie hiding (HasSrcSpan, HsModule, getLoc) descriptor :: PluginId -> PluginDescriptor IdeState descriptor pluginId = (defaultPluginDescriptor pluginId) { @@ -39,49 +38,56 @@ descriptor pluginId = (defaultPluginDescriptor pluginId) { renameProvider :: PluginMethodHandler IdeState TextDocumentRename renameProvider state pluginId (RenameParams (TextDocumentIdentifier uri) pos _prog newNameText) = response $ do - nfp <- safeGetNfp uri - oldName <- (handleMaybe "error: could not find name at pos" . listToMaybe) =<< - getNamesAtPos state pos nfp - refs <- refsAtName state nfp oldName - refFiles <- mapM safeGetNfp $ HS.toList $ HS.fromList [uri | Location uri _ <- refs] + nfp <- safeUriToNfp uri + oldName <- getNameAtPos state nfp pos + workspaceRefs <- refsAtName state nfp oldName + let filesRefs = groupOn locToUri workspaceRefs + getFileEdits = ap (getSrcEdits state . renameModRefs newNameText) (locToUri . head) - let newOccName = mkTcOcc $ T.unpack newNameText - nfpEdits <- mapMToSnd (getSrcEdits state (renameRefs refs newOccName)) refFiles - let uriEdits = HM.fromList $ map (Data.Bifunctor.first nfpToUri) nfpEdits - - pure $ WorkspaceEdit (Just uriEdits) Nothing Nothing + fileEdits <- mapM getFileEdits filesRefs + pure $ foldl1 (<>) fileEdits ------------------------------------------------------------------------------- -- Source renaming +-- | Compute a `WorkspaceEdit` by applying a given function to a `ParsedModule` at a given `Uri`. getSrcEdits :: - IdeState + (MonadLsp config m) => + IdeState -> #if MIN_VERSION_ghc(9,0,1) - -> (HsModule -> HsModule) + (HsModule -> HsModule) -> #else - -> (HsModule GhcPs -> HsModule GhcPs) + (HsModule GhcPs -> HsModule GhcPs) -> #endif - -> NormalizedFilePath - -> ExceptT String (LspT Config IO) (List TextEdit) -getSrcEdits state updateMod nfp = do - annPs <- handleMaybeM "error: parsed source" $ - liftIO $ runAction - "Rename.GetAnnotatedParsedModule" - state - (use GetAnnotatedParsedSource nfp) - let src = T.pack $ printA annPs - res = T.pack $ printA $ (fmap . fmap) updateMod annPs - pure $ makeDiffTextEdit src res - -renameRefs :: - [Location] - -> OccName + Uri -> + ExceptT String m WorkspaceEdit +getSrcEdits state updateMod uri = do + ccs <- lift getClientCapabilities + nfp <- safeUriToNfp uri + ParsedModule{pm_parsed_source = ps, pm_annotations = apiAnns} <- + handleMaybeM "Error: could not get parsed source" $ liftIO $ runAction + "Rename.GetParsedModuleWithComments" + state + (use GetParsedModuleWithComments nfp) + + let anns = relativiseApiAnns ps apiAnns + src = T.pack $ exactPrint ps anns + res = T.pack $ exactPrint (updateMod <$> ps) anns + + pure $ diffText ccs (uri, src) res IncludeDeletions + +-- | Replace a name at every given `Location` (in a given `HsModule`) with a given new name. +renameModRefs :: + T.Text -> + [Location] -> #if MIN_VERSION_ghc(9,0,1) - -> (HsModule -> HsModule) + HsModule + -> HsModule #else - -> (HsModule GhcPs -> HsModule GhcPs) + HsModule GhcPs + -> HsModule GhcPs #endif -renameRefs refs newOccName = everywhere $ mkT replace +renameModRefs newNameText refs = everywhere $ mkT replace where replace :: Located RdrName -> Located RdrName replace (L srcSpan oldRdrName) @@ -93,26 +99,31 @@ renameRefs refs newOccName = everywhere $ mkT replace Qual modName _ -> Qual modName newOccName _ -> Unqual newOccName + newOccName = mkTcOcc $ T.unpack newNameText + isRef :: SrcSpan -> Bool isRef = (`elem` refs) . fromJust . srcSpanToLocation ------------------------------------------------------------------------------- -- Reference finding +-- | Note: We only find exact name occurences (i.e. type reference "depth" is 0). refsAtName :: IdeState -> NormalizedFilePath -> Name -> ExceptT [Char] (LspT Config IO) [Location] refsAtName state nfp name = do ShakeExtras{hiedb} <- liftIO $ runAction "Rename.HieDb" state getShakeExtras - ast <- handleMaybeM "error: ast" $ liftIO $ runAction "" state $ useWithStale GetHieAst nfp - fileRefs <- handleMaybe "error: name references" $ getNameAstLocations name ast - let mod = nameModule_maybe name - dbRefs <- liftIO $ mapMaybe rowToLoc <$> findReferences - hiedb - True - (nameOccName name) - (moduleName <$> mod) - (moduleUnitId <$> mod) - [fromNormalizedFilePath nfp] - pure $ nubOrd $ fileRefs ++ dbRefs + ast <- safeGetHieAst state nfp + astRefs <- handleMaybe "Error: Could not get name AST references" $ getNameAstLocations name ast + dbRefs <- case nameModule_maybe name of + Nothing -> pure [] + Just mod -> liftIO $ mapMaybe rowToLoc <$> + findReferences + hiedb + True + (nameOccName name) + (Just $ moduleName mod) + (Just $ moduleUnitId mod) + [fromNormalizedFilePath nfp] + pure $ nubOrd $ astRefs ++ dbRefs getNameAstLocations :: Name -> (HieAstResult, PositionMapping) -> Maybe [Location] getNameAstLocations name (HAR _ _ rm _ _, mapping) = @@ -121,21 +132,25 @@ getNameAstLocations name (HAR _ _ rm _ _, mapping) = ------------------------------------------------------------------------------- -- Util -safeGetNfp :: (Monad m) => Uri -> ExceptT String m NormalizedFilePath -safeGetNfp uri = handleMaybe "error: uri" $ toNormalizedFilePath <$> uriToFilePath uri - -getNamesAtPos :: - IdeState - -> Position - -> NormalizedFilePath - -> ExceptT String (LspT Config IO) [Name] -getNamesAtPos state pos nfp = do - (HAR{hieAst}, mapping) <- handleMaybeM "error: ast" $ liftIO $ - runAction "Rename.GetHieAst" state $ useWithStale GetHieAst nfp - pure $ getAstNamesAtPoint hieAst pos mapping - -mapMToSnd :: Monad f => (a -> f b) -> [a] -> f [(a, b)] -mapMToSnd = liftM2 (<$>) zip . mapM +getNameAtPos :: IdeState -> NormalizedFilePath -> Position -> ExceptT String (LspT Config IO) Name +getNameAtPos state nfp pos = do + (HAR{hieAst}, mapping) <- safeGetHieAst state nfp + handleMaybe "Error: could not find name at position" $ listToMaybe $ + getAstNamesAtPoint hieAst pos mapping nfpToUri :: NormalizedFilePath -> Uri nfpToUri = filePathToUri . fromNormalizedFilePath + +safeUriToNfp :: (Monad m) => Uri -> ExceptT String m NormalizedFilePath +safeUriToNfp = handleMaybe "Error: Could not get uri" . fmap toNormalizedFilePath . uriToFilePath + +safeGetHieAst :: + MonadIO m => + IdeState -> + NormalizedFilePath -> + ExceptT String m (HieAstResult, PositionMapping) +safeGetHieAst state = handleMaybeM "Error: Could not get AST" . liftIO . + runAction "Rename.GetHieAst" state . useWithStale GetHieAst + +locToUri :: Location -> Uri +locToUri (Location uri _) = uri diff --git a/plugins/hls-rename-plugin/test/Main.hs b/plugins/hls-rename-plugin/test/Main.hs index 1425db0261..31baec621c 100644 --- a/plugins/hls-rename-plugin/test/Main.hs +++ b/plugins/hls-rename-plugin/test/Main.hs @@ -13,43 +13,43 @@ renamePlugin :: PluginDescriptor IdeState renamePlugin = Rename.descriptor "rename" tests :: TestTree -tests = testGroup "rename" - [ testGroup "Top-level renames" - [ goldenWithRename "data constructor" "DataConstructor" $ \doc -> do - rename doc (Position 0 15) "Op" - , goldenWithRename "exported function" "ExportedFunction" $ \doc -> do - rename doc (Position 2 1) "quux" - , goldenWithRename "function name" "FunctionName" $ \doc -> do - rename doc (Position 3 1) "baz" - , goldenWithRename "GADT" "Gadt" $ \doc -> do - rename doc (Position 6 37) "Expr" - , goldenWithRename "hidden function" "HiddenFunction" $ \doc -> do - rename doc (Position 0 32) "quux" - , goldenWithRename "imported function" "ImportedFunction" $ \doc -> do - rename doc (Position 3 8) "baz" - , goldenWithRename "import hiding" "ImportHiding" $ \doc -> do - rename doc (Position 0 22) "hiddenFoo" - , goldenWithRename "realign indentation" "Indentation" $ \doc -> do - rename doc (Position 0 2) "fooBarQuux" - , goldenWithRename "qualified as" "QualifiedAs" $ \doc -> do - rename doc (Position 3 10) "baz" - , goldenWithRename "qualified shadowing" "QualifiedShadowing" $ \doc -> do - rename doc (Position 3 12) "foobar" - , goldenWithRename "qualified function" "QualifiedFunction" $ \doc -> do - rename doc (Position 3 12) "baz" - , goldenWithRename "shadowed name" "ShadowedName" $ \doc -> do - rename doc (Position 1 1) "baz" - , goldenWithRename "type constructor" "TypeConstructor" $ \doc -> do - rename doc (Position 2 17) "BinaryTree" - ] - , testGroup "non Top-level renames" - [ goldenWithRename "function argument" "FunctionArgument" $ \doc -> do - rename doc (Position 3 4) "y" - , goldenWithRename "record field" "RecordField" $ \doc -> do - rename doc (Position 6 9) "number" - , goldenWithRename "type variable" "TypeVariable" $ \doc -> do - rename doc (Position 0 13) "b" - ] +tests = testGroup "Rename" + [ goldenWithRename "Data constructor" "DataConstructor" $ \doc -> do + rename doc (Position 0 15) "Op" + , goldenWithRename "Exported function" "ExportedFunction" $ \doc -> do + rename doc (Position 2 1) "quux" + , goldenWithRename "Function argument" "FunctionArgument" $ \doc -> do + rename doc (Position 3 4) "y" + , goldenWithRename "Function name" "FunctionName" $ \doc -> do + rename doc (Position 3 1) "baz" + , goldenWithRename "GADT" "Gadt" $ \doc -> do + rename doc (Position 6 37) "Expr" + , goldenWithRename "Hidden function" "HiddenFunction" $ \doc -> do + rename doc (Position 0 32) "quux" + , goldenWithRename "Imported function" "ImportedFunction" $ \doc -> do + rename doc (Position 3 8) "baz" + , goldenWithRename "Import hiding" "ImportHiding" $ \doc -> do + rename doc (Position 0 22) "hiddenFoo" + , goldenWithRename "Let expression" "LetExpression" $ \doc -> do + rename doc (Position 5 11) "foobar" + , goldenWithRename "Qualified as" "QualifiedAs" $ \doc -> do + rename doc (Position 3 10) "baz" + , goldenWithRename "Qualified shadowing" "QualifiedShadowing" $ \doc -> do + rename doc (Position 3 12) "foobar" + , goldenWithRename "Qualified function" "QualifiedFunction" $ \doc -> do + rename doc (Position 3 12) "baz" + , goldenWithRename "Realigns do block indentation" "RealignDo" $ \doc -> do + rename doc (Position 0 2) "fooBarQuux" + , goldenWithRename "Record field" "RecordField" $ \doc -> do + rename doc (Position 6 9) "number" + , goldenWithRename "Shadowed name" "ShadowedName" $ \doc -> do + rename doc (Position 1 1) "baz" + , goldenWithRename "Typeclass" "Typeclass" $ \doc -> do + rename doc (Position 8 15) "Equal" + , goldenWithRename "Type constructor" "TypeConstructor" $ \doc -> do + rename doc (Position 2 17) "BinaryTree" + , goldenWithRename "Type variable" "TypeVariable" $ \doc -> do + rename doc (Position 0 13) "b" ] goldenWithRename :: TestName -> FilePath -> (TextDocumentIdentifier -> Session ()) -> TestTree diff --git a/plugins/hls-rename-plugin/test/testdata/FunctionName.expected.hs b/plugins/hls-rename-plugin/test/testdata/FunctionName.expected.hs index a8cb8fc3bd..c02f55937b 100644 --- a/plugins/hls-rename-plugin/test/testdata/FunctionName.expected.hs +++ b/plugins/hls-rename-plugin/test/testdata/FunctionName.expected.hs @@ -1,6 +1,6 @@ main = do x <- return $ baz 42 return (baz x) -baz :: Int -> Int +baz, bar :: Int -> Int baz x = x + 1 bar = (+ 1) . baz diff --git a/plugins/hls-rename-plugin/test/testdata/FunctionName.hs b/plugins/hls-rename-plugin/test/testdata/FunctionName.hs index d13fa2c286..61bdc5e03c 100644 --- a/plugins/hls-rename-plugin/test/testdata/FunctionName.hs +++ b/plugins/hls-rename-plugin/test/testdata/FunctionName.hs @@ -1,6 +1,6 @@ main = do x <- return $ foo 42 return (foo x) -foo :: Int -> Int +foo, bar :: Int -> Int foo x = x + 1 bar = (+ 1) . foo diff --git a/plugins/hls-rename-plugin/test/testdata/LetExpression.expected.hs b/plugins/hls-rename-plugin/test/testdata/LetExpression.expected.hs new file mode 100644 index 0000000000..437fac2c96 --- /dev/null +++ b/plugins/hls-rename-plugin/test/testdata/LetExpression.expected.hs @@ -0,0 +1,10 @@ +module Let where + +import Foo + +bar :: Int +bar = let foobar = 5 in + foobar * foobar + +quux :: Int +quux = Foo.foo 4 diff --git a/plugins/hls-rename-plugin/test/testdata/LetExpression.hs b/plugins/hls-rename-plugin/test/testdata/LetExpression.hs new file mode 100644 index 0000000000..cbd5868de6 --- /dev/null +++ b/plugins/hls-rename-plugin/test/testdata/LetExpression.hs @@ -0,0 +1,10 @@ +module Let where + +import Foo + +bar :: Int +bar = let foo = 5 in + foo * foo + +quux :: Int +quux = Foo.foo 4 diff --git a/plugins/hls-rename-plugin/test/testdata/Indentation.expected.hs b/plugins/hls-rename-plugin/test/testdata/RealignDo.expected.hs similarity index 100% rename from plugins/hls-rename-plugin/test/testdata/Indentation.expected.hs rename to plugins/hls-rename-plugin/test/testdata/RealignDo.expected.hs diff --git a/plugins/hls-rename-plugin/test/testdata/Indentation.hs b/plugins/hls-rename-plugin/test/testdata/RealignDo.hs similarity index 100% rename from plugins/hls-rename-plugin/test/testdata/Indentation.hs rename to plugins/hls-rename-plugin/test/testdata/RealignDo.hs diff --git a/plugins/hls-rename-plugin/test/testdata/Typeclass.expected.hs b/plugins/hls-rename-plugin/test/testdata/Typeclass.expected.hs new file mode 100644 index 0000000000..73199ec83e --- /dev/null +++ b/plugins/hls-rename-plugin/test/testdata/Typeclass.expected.hs @@ -0,0 +1,10 @@ +module Typeclass where + +class Equal a where + equals :: a -> a -> Bool + +instance Equal Int where + equals = (==) + +allEqual :: Equal a => [a] -> Bool +allEqual = all =<< equals . head diff --git a/plugins/hls-rename-plugin/test/testdata/Typeclass.hs b/plugins/hls-rename-plugin/test/testdata/Typeclass.hs new file mode 100644 index 0000000000..57667128dd --- /dev/null +++ b/plugins/hls-rename-plugin/test/testdata/Typeclass.hs @@ -0,0 +1,10 @@ +module Typeclass where + +class Equality a where + equals :: a -> a -> Bool + +instance Equality Int where + equals = (==) + +allEqual :: Equality a => [a] -> Bool +allEqual = all =<< equals . head diff --git a/plugins/hls-rename-plugin/test/testdata/hie.yaml b/plugins/hls-rename-plugin/test/testdata/hie.yaml index 535eb08fd1..4c184b3c33 100644 --- a/plugins/hls-rename-plugin/test/testdata/hie.yaml +++ b/plugins/hls-rename-plugin/test/testdata/hie.yaml @@ -10,11 +10,13 @@ cradle: - "HiddenFunction" - "ImportHiding" - "ImportedFunction" - - "Indentation" + - "LetExpression" - "QualifiedAs" - "QualifiedFunction" - "QualifiedShadowing" + - "RealignDo" - "RecordField" - "ShadowedName" + - "TypeClass" - "TypeConstructor" - "TypeVariable" From aa599e274c6254cbf12da78986666ebb16daf694 Mon Sep 17 00:00:00 2001 From: Oliver Madine <30090176+OliverMadine@users.noreply.github.com> Date: Thu, 19 Aug 2021 09:03:20 +0100 Subject: [PATCH 12/13] replace GhcPlugins import with Name import --- plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs b/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs index 7e02739b8a..b9b1842c33 100644 --- a/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs +++ b/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs @@ -20,7 +20,7 @@ import Development.IDE.Core.PositionMapping import Development.IDE.Core.Shake import Development.IDE.GHC.Compat import Development.IDE.Spans.AtPoint -import GhcPlugins hiding ((<>)) +import Name import HieDb.Query import Ide.Plugin.Config import Ide.Plugin.Retrie hiding (descriptor) From 89d287c00dbcb4aaa896855b4b095fee9891ab64 Mon Sep 17 00:00:00 2001 From: Oliver Madine <30090176+OliverMadine@users.noreply.github.com> Date: Thu, 19 Aug 2021 09:25:58 +0100 Subject: [PATCH 13/13] use GHC.Types.Name for ghc >= 9.0.1 --- plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs b/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs index b9b1842c33..ce8e28428b 100644 --- a/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs +++ b/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs @@ -20,7 +20,11 @@ import Development.IDE.Core.PositionMapping import Development.IDE.Core.Shake import Development.IDE.GHC.Compat import Development.IDE.Spans.AtPoint +#if MIN_VERSION_ghc(9,0,1) +import GHC.Types.Name +#else import Name +#endif import HieDb.Query import Ide.Plugin.Config import Ide.Plugin.Retrie hiding (descriptor)