From 95d677b5287ed0d5de62c4068c3d94f1f683855d Mon Sep 17 00:00:00 2001 From: George Gerasev Date: Sun, 18 Aug 2024 19:42:34 +0300 Subject: [PATCH 01/16] + hover --- .../hls-cabal-plugin/src/Ide/Plugin/Cabal.hs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs index 317f48bb3a..fadc584596 100644 --- a/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs +++ b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs @@ -309,6 +309,30 @@ gotoDefinition ideState _ msgParam = do isSectionArgName name (Syntax.Section _ sectionArgName _) = name == CabalFields.onelineSectionArgs sectionArgName isSectionArgName _ _ = False +hover :: PluginMethodHandler IdeState LSP.Method_TextDocumentHover +hover ide _ msgParam = do + nfp <- getNormalizedFilePathE uri + (cabalFields, _) <- runActionE "cabal.cabal-hover" ide $ useWithStaleE ParseCabalFields nfp + let mCursorText = CabalFields.findTextWord cursor cabalFields + case mCursorText of + Nothing -> + pure $ InR Null + Just cursorText -> do + (gpd, _) <- runActionE "cabal.GPD" ide $ useWithStaleE ParseCabalFile nfp + let depsNames = map dependencyName $ allBuildDepends $ flattenPackageDescription gpd + if cursorText `elem` depsNames + then pure $ foundHover (Nothing, [cursorText <> "\n---\n" <> documentationText cursorText]) + else pure $ foundHover (Nothing, [cursorText]) + + -- TODO get package description and use `allBuildDepends` to check if hover is on a dependency + where + cursor = Types.lspPositionToCabalPosition (msgParam ^. JL.position) + uri = msgParam ^. JL.textDocument . JL.uri + dependencyName :: Dependency -> T.Text + dependencyName dep = T.pack $ unPackageName $ depPkgName dep + documentationText :: T.Text -> T.Text + documentationText package = "[Documentation](https://hackage.haskell.org/package/" <> package <> ")" + -- ---------------------------------------------------------------- -- Cabal file of Interest rules and global variable -- ---------------------------------------------------------------- From 5f80c6ad212f7d608b76201b3412de60978cc56b Mon Sep 17 00:00:00 2001 From: George Gerasev Date: Sun, 18 Aug 2024 19:43:44 +0300 Subject: [PATCH 02/16] + hover handler --- plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs index fadc584596..33f887b8d7 100644 --- a/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs +++ b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs @@ -98,6 +98,7 @@ descriptor recorder plId = , mkPluginHandler LSP.SMethod_TextDocumentDocumentSymbol moduleOutline , mkPluginHandler LSP.SMethod_TextDocumentCodeAction $ fieldSuggestCodeAction recorder , mkPluginHandler LSP.SMethod_TextDocumentDefinition gotoDefinition + , mkPluginHandler LSP.SMethod_TextDocumentHover hover ] , pluginNotificationHandlers = mconcat From c12aa1f9a118cc754fe36fa7083dcd787f56f627 Mon Sep 17 00:00:00 2001 From: George Gerasev Date: Sun, 18 Aug 2024 19:55:03 +0300 Subject: [PATCH 03/16] working prototype --- ghcide/src/Development/IDE/LSP/HoverDefinition.hs | 1 + plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/ghcide/src/Development/IDE/LSP/HoverDefinition.hs b/ghcide/src/Development/IDE/LSP/HoverDefinition.hs index aea3449bf3..1a7035cc95 100644 --- a/ghcide/src/Development/IDE/LSP/HoverDefinition.hs +++ b/ghcide/src/Development/IDE/LSP/HoverDefinition.hs @@ -7,6 +7,7 @@ module Development.IDE.LSP.HoverDefinition ( Log(..) -- * For haskell-language-server , hover + , foundHover , gotoDefinition , gotoTypeDefinition , documentHighlight diff --git a/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs index 33f887b8d7..93ce068d70 100644 --- a/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs +++ b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs @@ -52,6 +52,13 @@ import qualified Language.LSP.Protocol.Lens as JL import qualified Language.LSP.Protocol.Message as LSP import Language.LSP.Protocol.Types import qualified Language.LSP.VFS as VFS +import Development.IDE.Core.PluginUtils (useWithStaleE, runActionE) +import Ide.Plugin.Error (getNormalizedFilePathE) +import Distribution.PackageDescription.Configuration (flattenPackageDescription) +import Distribution.Package (Dependency()) +import Distribution.PackageDescription (depPkgName, unPackageName, allBuildDepends) +import Development.IDE.LSP.HoverDefinition (foundHover) + data Log = LogModificationTime NormalizedFilePath FileVersion From c2e769b50f92f6deb60391fa0311d5b1ecb1c830 Mon Sep 17 00:00:00 2001 From: George Gerasev Date: Sun, 18 Aug 2024 20:08:20 +0300 Subject: [PATCH 04/16] bugfix --- plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs index 93ce068d70..007558a4a6 100644 --- a/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs +++ b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs @@ -329,7 +329,7 @@ hover ide _ msgParam = do (gpd, _) <- runActionE "cabal.GPD" ide $ useWithStaleE ParseCabalFile nfp let depsNames = map dependencyName $ allBuildDepends $ flattenPackageDescription gpd if cursorText `elem` depsNames - then pure $ foundHover (Nothing, [cursorText <> "\n---\n" <> documentationText cursorText]) + then pure $ foundHover (Nothing, [cursorText <> "\n", documentationText cursorText]) else pure $ foundHover (Nothing, [cursorText]) -- TODO get package description and use `allBuildDepends` to check if hover is on a dependency From 28af58bd24e4fffa735373c402ac2c1dbdf66e5f Mon Sep 17 00:00:00 2001 From: George Gerasev Date: Sun, 18 Aug 2024 20:19:04 +0300 Subject: [PATCH 05/16] rm TODO --- plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs index 007558a4a6..3f061c568a 100644 --- a/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs +++ b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs @@ -331,8 +331,6 @@ hover ide _ msgParam = do if cursorText `elem` depsNames then pure $ foundHover (Nothing, [cursorText <> "\n", documentationText cursorText]) else pure $ foundHover (Nothing, [cursorText]) - - -- TODO get package description and use `allBuildDepends` to check if hover is on a dependency where cursor = Types.lspPositionToCabalPosition (msgParam ^. JL.position) uri = msgParam ^. JL.textDocument . JL.uri From 729b4e82e27455468d988a92c0696179c935d798 Mon Sep 17 00:00:00 2001 From: George Gerasev Date: Tue, 20 Aug 2024 02:17:49 +0300 Subject: [PATCH 06/16] + tests --- .../hls-cabal-plugin/src/Ide/Plugin/Cabal.hs | 27 ++++++++-- plugins/hls-cabal-plugin/test/Main.hs | 51 +++++++++++++++++++ .../test/testdata/hover/hover-deps.cabal | 10 ++++ 3 files changed, 83 insertions(+), 5 deletions(-) create mode 100644 plugins/hls-cabal-plugin/test/testdata/hover/hover-deps.cabal diff --git a/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs index 3f061c568a..b5393ca6f0 100644 --- a/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs +++ b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs @@ -52,12 +52,12 @@ import qualified Language.LSP.Protocol.Lens as JL import qualified Language.LSP.Protocol.Message as LSP import Language.LSP.Protocol.Types import qualified Language.LSP.VFS as VFS -import Development.IDE.Core.PluginUtils (useWithStaleE, runActionE) -import Ide.Plugin.Error (getNormalizedFilePathE) import Distribution.PackageDescription.Configuration (flattenPackageDescription) import Distribution.Package (Dependency()) import Distribution.PackageDescription (depPkgName, unPackageName, allBuildDepends) import Development.IDE.LSP.HoverDefinition (foundHover) +import Text.Regex.TDFA +import Debug.Trace data Log @@ -328,14 +328,31 @@ hover ide _ msgParam = do Just cursorText -> do (gpd, _) <- runActionE "cabal.GPD" ide $ useWithStaleE ParseCabalFile nfp let depsNames = map dependencyName $ allBuildDepends $ flattenPackageDescription gpd - if cursorText `elem` depsNames - then pure $ foundHover (Nothing, [cursorText <> "\n", documentationText cursorText]) - else pure $ foundHover (Nothing, [cursorText]) + mText = filterVersion cursorText + case mText of + Nothing -> pure $ foundHover (Nothing, [cursorText]) + Just txt -> + if txt `elem` depsNames + then pure $ foundHover (Nothing, [txt <> "\n", documentationText txt]) + else pure $ foundHover (Nothing, [txt]) where cursor = Types.lspPositionToCabalPosition (msgParam ^. JL.position) uri = msgParam ^. JL.textDocument . JL.uri + dependencyName :: Dependency -> T.Text dependencyName dep = T.pack $ unPackageName $ depPkgName dep + + filterVersion :: T.Text -> Maybe T.Text + filterVersion msg = getMatch (msg =~ regex) + where + regex :: T.Text + regex = "([a-zA-Z0-9-]*[a-zA-Z0-9]).*" + + getMatch :: (T.Text, T.Text, T.Text, [T.Text]) -> Maybe T.Text + getMatch (_, _, _, []) = Nothing + getMatch (_, _, _, [dependency]) = Just dependency + getMatch (_, _, _, _) = Nothing -- impossible case + documentationText :: T.Text -> T.Text documentationText package = "[Documentation](https://hackage.haskell.org/package/" <> package <> ")" diff --git a/plugins/hls-cabal-plugin/test/Main.hs b/plugins/hls-cabal-plugin/test/Main.hs index 2009352bbd..38bde4695b 100644 --- a/plugins/hls-cabal-plugin/test/Main.hs +++ b/plugins/hls-cabal-plugin/test/Main.hs @@ -38,6 +38,7 @@ main = do , outlineTests , codeActionTests , gotoDefinitionTests + , hoverTests ] -- ------------------------------------------------------------------------ @@ -282,3 +283,53 @@ gotoDefinitionTests = testGroup "Goto Definition" doc <- openDoc "simple-with-common.cabal" "cabal" empty <- getDefinitions doc cursorPos liftIO $ empty @?= (InR $ InR LSP.Null) + +-- ---------------------------------------------------------------------------- +-- Hover Tests +-- ---------------------------------------------------------------------------- + +hoverTests :: TestTree +hoverTests = testGroup "Hover" + [ hoverOnDependencyTests + ] + +hoverOnDependencyTests :: TestTree +hoverOnDependencyTests = testGroup "Hover Dependency" + [ hoverContainsTest "base with separated version" "hover-deps.cabal" (Position 6 25) "[Documentation](https://hackage.haskell.org/package/base)" + , hoverContainsTest "aeson with not separated version " "hover-deps.cabal" (Position 7 25) "[Documentation](https://hackage.haskell.org/package/aeson)" + , hoverContainsTest "lens no version" "hover-deps.cabal" (Position 7 42) "[Documentation](https://hackage.haskell.org/package/lens)" + + , hoverNotContainsTest "name has no documentation" "hover-deps.cabal" (Position 1 25) "[Documentation]" + , hoverNotContainsTest "exposed-modules has no documentation" "hover-deps.cabal" (Position 5 25) "[Documentation]" + , hoverNotContainsTest "hs-source-dirs has no documentation" "hover-deps.cabal" (Position 8 25) "[Documentation]" + ] + where + hoverContainsTest :: TestName -> FilePath -> Position -> T.Text -> TestTree + hoverContainsTest testName cabalFile pos containedText = + runCabalTestCaseSession testName "hover" $ do + doc <- openDoc cabalFile "cabal" + h <- getHover doc pos + case h of + Nothing -> liftIO $ assertFailure "No hover" + Just (Hover contents _) -> case contents of + InL (MarkupContent _ txt) -> do + liftIO + $ assertBool ("Failed to find `" <> T.unpack containedText <> "` in hover message: " <> T.unpack txt) + $ containedText `T.isInfixOf` txt + _ -> liftIO $ assertFailure "Unexpected content type" + closeDoc doc + + hoverNotContainsTest :: TestName -> FilePath -> Position -> T.Text -> TestTree + hoverNotContainsTest testName cabalFile pos containedText = + runCabalTestCaseSession testName "hover" $ do + doc <- openDoc cabalFile "cabal" + h <- getHover doc pos + case h of + Nothing -> liftIO $ assertFailure "No hover" + Just (Hover contents _) -> case contents of + InL (MarkupContent _ txt) -> do + liftIO + $ assertBool ("Found `" <> T.unpack containedText <> "` in hover message: " <> T.unpack txt) + $ not (containedText `T.isInfixOf` txt) + _ -> liftIO $ assertFailure "Unexpected content type" + closeDoc doc diff --git a/plugins/hls-cabal-plugin/test/testdata/hover/hover-deps.cabal b/plugins/hls-cabal-plugin/test/testdata/hover/hover-deps.cabal new file mode 100644 index 0000000000..ddc4a6107a --- /dev/null +++ b/plugins/hls-cabal-plugin/test/testdata/hover/hover-deps.cabal @@ -0,0 +1,10 @@ +cabal-version: 3.0 +name: hover-deps +version: 0.1.0.0 + +library + exposed-modules: Module + build-depends: base ^>=4.14.3.0 + , aeson==1.0.0.0 , lens + hs-source-dirs: src + default-language: Haskell2010 From 109f04c3c07d7807131ff08e9b8c5d801dd929d8 Mon Sep 17 00:00:00 2001 From: George Gerasev Date: Tue, 20 Aug 2024 02:20:22 +0300 Subject: [PATCH 07/16] docs --- plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs index b5393ca6f0..493cb8a814 100644 --- a/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs +++ b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs @@ -317,6 +317,11 @@ gotoDefinition ideState _ msgParam = do isSectionArgName name (Syntax.Section _ sectionArgName _) = name == CabalFields.onelineSectionArgs sectionArgName isSectionArgName _ _ = False +-- | CodeActions for hover messages. +-- +-- Provides a CodeAction for displaying message on hover. +-- If found that the filtered hover message is a dependency, +-- adds a Documentation link. hover :: PluginMethodHandler IdeState LSP.Method_TextDocumentHover hover ide _ msgParam = do nfp <- getNormalizedFilePathE uri From 8c740dc1b3894e7e907460252c7d5ec6c318d197 Mon Sep 17 00:00:00 2001 From: George Gerasev Date: Tue, 20 Aug 2024 21:02:47 +0300 Subject: [PATCH 08/16] requested changes --- .../hls-cabal-plugin/src/Ide/Plugin/Cabal.hs | 181 +++++++++--------- plugins/hls-cabal-plugin/test/Main.hs | 19 +- 2 files changed, 98 insertions(+), 102 deletions(-) diff --git a/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs index 493cb8a814..923caf909b 100644 --- a/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs +++ b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs @@ -8,56 +8,59 @@ module Ide.Plugin.Cabal (descriptor, Log (..)) where import Control.Concurrent.Strict import Control.DeepSeq -import Control.Lens ((^.)) +import Control.Lens ((^.)) import Control.Monad.Extra import Control.Monad.IO.Class import Control.Monad.Trans.Class -import Control.Monad.Trans.Maybe (runMaybeT) -import qualified Data.ByteString as BS +import Control.Monad.Trans.Maybe (runMaybeT) +import qualified Data.ByteString as BS import Data.Hashable -import Data.HashMap.Strict (HashMap) -import qualified Data.HashMap.Strict as HashMap -import Data.List (find) -import qualified Data.List.NonEmpty as NE -import qualified Data.Maybe as Maybe -import qualified Data.Text as T -import qualified Data.Text.Encoding as Encoding +import Data.HashMap.Strict (HashMap) +import qualified Data.HashMap.Strict as HashMap +import Data.List (find) +import qualified Data.List.NonEmpty as NE +import qualified Data.Maybe as Maybe +import qualified Data.Text as T +import qualified Data.Text.Encoding as Encoding import Data.Typeable -import Development.IDE as D +import Debug.Trace +import Development.IDE as D import Development.IDE.Core.PluginUtils -import Development.IDE.Core.Shake (restartShakeSession) -import qualified Development.IDE.Core.Shake as Shake -import Development.IDE.Graph (Key, alwaysRerun) -import qualified Development.IDE.Plugin.Completions.Logic as Ghcide -import Development.IDE.Types.Shake (toKey) -import qualified Distribution.Fields as Syntax -import qualified Distribution.Parsec.Position as Syntax +import Development.IDE.Core.Shake (restartShakeSession) +import qualified Development.IDE.Core.Shake as Shake +import Development.IDE.Graph (Key, + alwaysRerun) +import Development.IDE.LSP.HoverDefinition (foundHover) +import qualified Development.IDE.Plugin.Completions.Logic as Ghcide +import Development.IDE.Types.Shake (toKey) +import qualified Distribution.Fields as Syntax +import Distribution.Package (Dependency) +import Distribution.PackageDescription (allBuildDepends, + depPkgName, + unPackageName) +import Distribution.PackageDescription.Configuration (flattenPackageDescription) +import qualified Distribution.Parsec.Position as Syntax import GHC.Generics -import Ide.Plugin.Cabal.Completion.CabalFields as CabalFields -import qualified Ide.Plugin.Cabal.Completion.Completer.Types as CompleterTypes -import qualified Ide.Plugin.Cabal.Completion.Completions as Completions -import Ide.Plugin.Cabal.Completion.Types (ParseCabalCommonSections (ParseCabalCommonSections), - ParseCabalFields (..), - ParseCabalFile (..)) -import qualified Ide.Plugin.Cabal.Completion.Types as Types -import qualified Ide.Plugin.Cabal.Diagnostics as Diagnostics -import qualified Ide.Plugin.Cabal.FieldSuggest as FieldSuggest -import qualified Ide.Plugin.Cabal.LicenseSuggest as LicenseSuggest -import Ide.Plugin.Cabal.Orphans () +import Ide.Plugin.Cabal.Completion.CabalFields as CabalFields +import qualified Ide.Plugin.Cabal.Completion.Completer.Types as CompleterTypes +import qualified Ide.Plugin.Cabal.Completion.Completions as Completions +import Ide.Plugin.Cabal.Completion.Types (ParseCabalCommonSections (ParseCabalCommonSections), + ParseCabalFields (..), + ParseCabalFile (..)) +import qualified Ide.Plugin.Cabal.Completion.Types as Types +import qualified Ide.Plugin.Cabal.Diagnostics as Diagnostics +import qualified Ide.Plugin.Cabal.FieldSuggest as FieldSuggest +import qualified Ide.Plugin.Cabal.LicenseSuggest as LicenseSuggest +import Ide.Plugin.Cabal.Orphans () import Ide.Plugin.Cabal.Outline -import qualified Ide.Plugin.Cabal.Parse as Parse +import qualified Ide.Plugin.Cabal.Parse as Parse import Ide.Plugin.Error import Ide.Types -import qualified Language.LSP.Protocol.Lens as JL -import qualified Language.LSP.Protocol.Message as LSP +import qualified Language.LSP.Protocol.Lens as JL +import qualified Language.LSP.Protocol.Message as LSP import Language.LSP.Protocol.Types -import qualified Language.LSP.VFS as VFS -import Distribution.PackageDescription.Configuration (flattenPackageDescription) -import Distribution.Package (Dependency()) -import Distribution.PackageDescription (depPkgName, unPackageName, allBuildDepends) -import Development.IDE.LSP.HoverDefinition (foundHover) +import qualified Language.LSP.VFS as VFS import Text.Regex.TDFA -import Debug.Trace data Log @@ -299,67 +302,67 @@ fieldSuggestCodeAction recorder ide _ (CodeActionParams _ _ (TextDocumentIdentif -- TODO: Support more definitions than sections. gotoDefinition :: PluginMethodHandler IdeState LSP.Method_TextDocumentDefinition gotoDefinition ideState _ msgParam = do - nfp <- getNormalizedFilePathE uri - cabalFields <- runActionE "cabal-plugin.commonSections" ideState $ useE ParseCabalFields nfp - case CabalFields.findTextWord cursor cabalFields of - Nothing -> - pure $ InR $ InR Null - Just cursorText -> do - commonSections <- runActionE "cabal-plugin.commonSections" ideState $ useE ParseCabalCommonSections nfp - case find (isSectionArgName cursorText) commonSections of - Nothing -> - pure $ InR $ InR Null - Just commonSection -> do - pure $ InL $ Definition $ InL $ Location uri $ CabalFields.getFieldLSPRange commonSection - where - cursor = Types.lspPositionToCabalPosition (msgParam ^. JL.position) - uri = msgParam ^. JL.textDocument . JL.uri - isSectionArgName name (Syntax.Section _ sectionArgName _) = name == CabalFields.onelineSectionArgs sectionArgName - isSectionArgName _ _ = False - --- | CodeActions for hover messages. + nfp <- getNormalizedFilePathE uri + cabalFields <- runActionE "cabal-plugin.commonSections" ideState $ useE ParseCabalFields nfp + case CabalFields.findTextWord cursor cabalFields of + Nothing -> + pure $ InR $ InR Null + Just cursorText -> do + commonSections <- runActionE "cabal-plugin.commonSections" ideState $ useE ParseCabalCommonSections nfp + case find (isSectionArgName cursorText) commonSections of + Nothing -> + pure $ InR $ InR Null + Just commonSection -> do + pure $ InL $ Definition $ InL $ Location uri $ CabalFields.getFieldLSPRange commonSection + where + cursor = Types.lspPositionToCabalPosition (msgParam ^. JL.position) + uri = msgParam ^. JL.textDocument . JL.uri + isSectionArgName name (Syntax.Section _ sectionArgName _) = name == CabalFields.onelineSectionArgs sectionArgName + isSectionArgName _ _ = False + +-- | Handler for hover messages. -- --- Provides a CodeAction for displaying message on hover. +-- Provides a Handler for displaying message on hover. -- If found that the filtered hover message is a dependency, -- adds a Documentation link. hover :: PluginMethodHandler IdeState LSP.Method_TextDocumentHover hover ide _ msgParam = do - nfp <- getNormalizedFilePathE uri - (cabalFields, _) <- runActionE "cabal.cabal-hover" ide $ useWithStaleE ParseCabalFields nfp - let mCursorText = CabalFields.findTextWord cursor cabalFields - case mCursorText of - Nothing -> - pure $ InR Null - Just cursorText -> do - (gpd, _) <- runActionE "cabal.GPD" ide $ useWithStaleE ParseCabalFile nfp - let depsNames = map dependencyName $ allBuildDepends $ flattenPackageDescription gpd - mText = filterVersion cursorText - case mText of - Nothing -> pure $ foundHover (Nothing, [cursorText]) - Just txt -> - if txt `elem` depsNames - then pure $ foundHover (Nothing, [txt <> "\n", documentationText txt]) - else pure $ foundHover (Nothing, [txt]) + nfp <- getNormalizedFilePathE uri + (cabalFields, _) <- runActionE "cabal.cabal-hover" ide $ useWithStaleE ParseCabalFields nfp + let mCursorText = CabalFields.findTextWord cursor cabalFields + case mCursorText of + Nothing -> + pure $ InR Null + Just cursorText -> do + (gpd, _) <- runActionE "cabal.GPD" ide $ useWithStaleE ParseCabalFile nfp + let depsNames = map dependencyName $ allBuildDepends $ flattenPackageDescription gpd + mText = filterVersion cursorText + case mText of + Nothing -> pure $ InR Null + Just txt -> + if txt `elem` depsNames + then pure $ foundHover (Nothing, [txt <> "\n", documentationText txt]) + else pure $ InR Null where - cursor = Types.lspPositionToCabalPosition (msgParam ^. JL.position) - uri = msgParam ^. JL.textDocument . JL.uri + cursor = Types.lspPositionToCabalPosition (msgParam ^. JL.position) + uri = msgParam ^. JL.textDocument . JL.uri - dependencyName :: Dependency -> T.Text - dependencyName dep = T.pack $ unPackageName $ depPkgName dep + dependencyName :: Dependency -> T.Text + dependencyName dep = T.pack $ unPackageName $ depPkgName dep - filterVersion :: T.Text -> Maybe T.Text - filterVersion msg = getMatch (msg =~ regex) - where - regex :: T.Text - regex = "([a-zA-Z0-9-]*[a-zA-Z0-9]).*" + filterVersion :: T.Text -> Maybe T.Text + filterVersion msg = getMatch (msg =~ regex) + where + regex :: T.Text + regex = "([a-zA-Z0-9-]*[a-zA-Z0-9])" - getMatch :: (T.Text, T.Text, T.Text, [T.Text]) -> Maybe T.Text - getMatch (_, _, _, []) = Nothing - getMatch (_, _, _, [dependency]) = Just dependency - getMatch (_, _, _, _) = Nothing -- impossible case + getMatch :: (T.Text, T.Text, T.Text, [T.Text]) -> Maybe T.Text + getMatch (_, _, _, []) = Nothing + getMatch (_, _, _, [dependency]) = Just dependency + getMatch (_, _, _, _) = Nothing -- impossible case - documentationText :: T.Text -> T.Text - documentationText package = "[Documentation](https://hackage.haskell.org/package/" <> package <> ")" + documentationText :: T.Text -> T.Text + documentationText package = "[Documentation](https://hackage.haskell.org/package/" <> package <> ")" -- ---------------------------------------------------------------- -- Cabal file of Interest rules and global variable diff --git a/plugins/hls-cabal-plugin/test/Main.hs b/plugins/hls-cabal-plugin/test/Main.hs index 38bde4695b..f0c5e01ddd 100644 --- a/plugins/hls-cabal-plugin/test/Main.hs +++ b/plugins/hls-cabal-plugin/test/Main.hs @@ -299,9 +299,9 @@ hoverOnDependencyTests = testGroup "Hover Dependency" , hoverContainsTest "aeson with not separated version " "hover-deps.cabal" (Position 7 25) "[Documentation](https://hackage.haskell.org/package/aeson)" , hoverContainsTest "lens no version" "hover-deps.cabal" (Position 7 42) "[Documentation](https://hackage.haskell.org/package/lens)" - , hoverNotContainsTest "name has no documentation" "hover-deps.cabal" (Position 1 25) "[Documentation]" - , hoverNotContainsTest "exposed-modules has no documentation" "hover-deps.cabal" (Position 5 25) "[Documentation]" - , hoverNotContainsTest "hs-source-dirs has no documentation" "hover-deps.cabal" (Position 8 25) "[Documentation]" + , hoverIsNullTest "name has no documentation" "hover-deps.cabal" (Position 1 25) + , hoverIsNullTest "exposed-modules has no documentation" "hover-deps.cabal" (Position 5 25) + , hoverIsNullTest "hs-source-dirs has no documentation" "hover-deps.cabal" (Position 8 25) ] where hoverContainsTest :: TestName -> FilePath -> Position -> T.Text -> TestTree @@ -319,17 +319,10 @@ hoverOnDependencyTests = testGroup "Hover Dependency" _ -> liftIO $ assertFailure "Unexpected content type" closeDoc doc - hoverNotContainsTest :: TestName -> FilePath -> Position -> T.Text -> TestTree - hoverNotContainsTest testName cabalFile pos containedText = + hoverIsNullTest :: TestName -> FilePath -> Position -> TestTree + hoverIsNullTest testName cabalFile pos = runCabalTestCaseSession testName "hover" $ do doc <- openDoc cabalFile "cabal" h <- getHover doc pos - case h of - Nothing -> liftIO $ assertFailure "No hover" - Just (Hover contents _) -> case contents of - InL (MarkupContent _ txt) -> do - liftIO - $ assertBool ("Found `" <> T.unpack containedText <> "` in hover message: " <> T.unpack txt) - $ not (containedText `T.isInfixOf` txt) - _ -> liftIO $ assertFailure "Unexpected content type" + liftIO $ assertBool ("Found hover `" <> show h <> "`") $ Maybe.isNothing h closeDoc doc From cbeb3d51fd87bdbed5638ca78fd6c234538c7f5a Mon Sep 17 00:00:00 2001 From: George Gerasev Date: Tue, 20 Aug 2024 21:03:42 +0300 Subject: [PATCH 09/16] - Debug.Trace --- plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs index 923caf909b..dd05eb5eb1 100644 --- a/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs +++ b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs @@ -23,7 +23,6 @@ import qualified Data.Maybe as Maybe import qualified Data.Text as T import qualified Data.Text.Encoding as Encoding import Data.Typeable -import Debug.Trace import Development.IDE as D import Development.IDE.Core.PluginUtils import Development.IDE.Core.Shake (restartShakeSession) From a955e58272df5c00678f942b97119b8a8e283a5b Mon Sep 17 00:00:00 2001 From: George Gerasev Date: Tue, 20 Aug 2024 23:17:37 +0300 Subject: [PATCH 10/16] schema --- test/testdata/schema/ghc94/default-config.golden.json | 1 + .../schema/ghc94/vscode-extension-schema.golden.json | 6 ++++++ test/testdata/schema/ghc96/default-config.golden.json | 1 + .../schema/ghc96/vscode-extension-schema.golden.json | 6 ++++++ test/testdata/schema/ghc98/default-config.golden.json | 1 + .../schema/ghc98/vscode-extension-schema.golden.json | 6 ++++++ 6 files changed, 21 insertions(+) diff --git a/test/testdata/schema/ghc94/default-config.golden.json b/test/testdata/schema/ghc94/default-config.golden.json index 5f881ff00e..773fe7a0d4 100644 --- a/test/testdata/schema/ghc94/default-config.golden.json +++ b/test/testdata/schema/ghc94/default-config.golden.json @@ -12,6 +12,7 @@ "codeActionsOn": true, "completionOn": true, "diagnosticsOn": true, + "hoverOn": true, "symbolsOn": true }, "cabal-fmt": { diff --git a/test/testdata/schema/ghc94/vscode-extension-schema.golden.json b/test/testdata/schema/ghc94/vscode-extension-schema.golden.json index 5da4a27dd6..b65a452ea7 100644 --- a/test/testdata/schema/ghc94/vscode-extension-schema.golden.json +++ b/test/testdata/schema/ghc94/vscode-extension-schema.golden.json @@ -35,6 +35,12 @@ "scope": "resource", "type": "boolean" }, + "haskell.plugin.cabal.hoverOn": { + "default": true, + "description": "Enables cabal hover", + "scope": "resource", + "type": "boolean" + }, "haskell.plugin.cabal.symbolsOn": { "default": true, "description": "Enables cabal symbols", diff --git a/test/testdata/schema/ghc96/default-config.golden.json b/test/testdata/schema/ghc96/default-config.golden.json index 5f881ff00e..773fe7a0d4 100644 --- a/test/testdata/schema/ghc96/default-config.golden.json +++ b/test/testdata/schema/ghc96/default-config.golden.json @@ -12,6 +12,7 @@ "codeActionsOn": true, "completionOn": true, "diagnosticsOn": true, + "hoverOn": true, "symbolsOn": true }, "cabal-fmt": { diff --git a/test/testdata/schema/ghc96/vscode-extension-schema.golden.json b/test/testdata/schema/ghc96/vscode-extension-schema.golden.json index 5da4a27dd6..b65a452ea7 100644 --- a/test/testdata/schema/ghc96/vscode-extension-schema.golden.json +++ b/test/testdata/schema/ghc96/vscode-extension-schema.golden.json @@ -35,6 +35,12 @@ "scope": "resource", "type": "boolean" }, + "haskell.plugin.cabal.hoverOn": { + "default": true, + "description": "Enables cabal hover", + "scope": "resource", + "type": "boolean" + }, "haskell.plugin.cabal.symbolsOn": { "default": true, "description": "Enables cabal symbols", diff --git a/test/testdata/schema/ghc98/default-config.golden.json b/test/testdata/schema/ghc98/default-config.golden.json index 5f881ff00e..773fe7a0d4 100644 --- a/test/testdata/schema/ghc98/default-config.golden.json +++ b/test/testdata/schema/ghc98/default-config.golden.json @@ -12,6 +12,7 @@ "codeActionsOn": true, "completionOn": true, "diagnosticsOn": true, + "hoverOn": true, "symbolsOn": true }, "cabal-fmt": { diff --git a/test/testdata/schema/ghc98/vscode-extension-schema.golden.json b/test/testdata/schema/ghc98/vscode-extension-schema.golden.json index 5da4a27dd6..b65a452ea7 100644 --- a/test/testdata/schema/ghc98/vscode-extension-schema.golden.json +++ b/test/testdata/schema/ghc98/vscode-extension-schema.golden.json @@ -35,6 +35,12 @@ "scope": "resource", "type": "boolean" }, + "haskell.plugin.cabal.hoverOn": { + "default": true, + "description": "Enables cabal hover", + "scope": "resource", + "type": "boolean" + }, "haskell.plugin.cabal.symbolsOn": { "default": true, "description": "Enables cabal symbols", From d7255411d610cf23dac8a4d845c8ae132aed0c40 Mon Sep 17 00:00:00 2001 From: Georgii Gerasev <54953043+VenInf@users.noreply.github.com> Date: Thu, 22 Aug 2024 19:23:52 +0300 Subject: [PATCH 11/16] Apply suggestions from code review Co-authored-by: fendor --- plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs index dd05eb5eb1..f32f9490da 100644 --- a/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs +++ b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs @@ -328,15 +328,13 @@ hover :: PluginMethodHandler IdeState LSP.Method_TextDocumentHover hover ide _ msgParam = do nfp <- getNormalizedFilePathE uri (cabalFields, _) <- runActionE "cabal.cabal-hover" ide $ useWithStaleE ParseCabalFields nfp - let mCursorText = CabalFields.findTextWord cursor cabalFields - case mCursorText of + case CabalFields.findTextWord cursor cabalFields of Nothing -> pure $ InR Null Just cursorText -> do (gpd, _) <- runActionE "cabal.GPD" ide $ useWithStaleE ParseCabalFile nfp let depsNames = map dependencyName $ allBuildDepends $ flattenPackageDescription gpd - mText = filterVersion cursorText - case mText of + case filterVersion cursorText of Nothing -> pure $ InR Null Just txt -> if txt `elem` depsNames From ecd8828eae6a9d3e4f1a2e1744ef280d804a4e43 Mon Sep 17 00:00:00 2001 From: George Gerasev Date: Thu, 22 Aug 2024 22:10:31 +0300 Subject: [PATCH 12/16] resolve merge issues --- .../hls-cabal-plugin/src/Ide/Plugin/Cabal.hs | 63 +------------------ plugins/hls-cabal-plugin/test/Main.hs | 53 ---------------- 2 files changed, 3 insertions(+), 113 deletions(-) diff --git a/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs index 36b2a20fd6..8030e89251 100644 --- a/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs +++ b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs @@ -17,7 +17,6 @@ import qualified Data.ByteString as BS import Data.Hashable import Data.HashMap.Strict (HashMap) import qualified Data.HashMap.Strict as HashMap -import Data.List (find) import qualified Data.List.NonEmpty as NE import qualified Data.Maybe as Maybe import qualified Data.Text as T @@ -47,6 +46,7 @@ import Ide.Plugin.Cabal.Completion.Types (ParseCabalCommon ParseCabalFields (..), ParseCabalFile (..)) import qualified Ide.Plugin.Cabal.Completion.Types as Types +import Ide.Plugin.Cabal.Definition (gotoDefinition) import qualified Ide.Plugin.Cabal.Diagnostics as Diagnostics import qualified Ide.Plugin.Cabal.FieldSuggest as FieldSuggest import qualified Ide.Plugin.Cabal.LicenseSuggest as LicenseSuggest @@ -54,35 +54,6 @@ import Ide.Plugin.Cabal.Orphans () import Ide.Plugin.Cabal.Outline import qualified Ide.Plugin.Cabal.Parse as Parse import Ide.Plugin.Error -import Data.HashMap.Strict (HashMap) -import qualified Data.HashMap.Strict as HashMap -import qualified Data.List.NonEmpty as NE -import qualified Data.Maybe as Maybe -import qualified Data.Text as T -import qualified Data.Text.Encoding as Encoding -import Data.Typeable -import Development.IDE as D -import Development.IDE.Core.Shake (restartShakeSession) -import qualified Development.IDE.Core.Shake as Shake -import Development.IDE.Graph (Key, alwaysRerun) -import qualified Development.IDE.Plugin.Completions.Logic as Ghcide -import Development.IDE.Types.Shake (toKey) -import qualified Distribution.Fields as Syntax -import qualified Distribution.Parsec.Position as Syntax -import GHC.Generics -import qualified Ide.Plugin.Cabal.Completion.Completer.Types as CompleterTypes -import qualified Ide.Plugin.Cabal.Completion.Completions as Completions -import Ide.Plugin.Cabal.Completion.Types (ParseCabalCommonSections (ParseCabalCommonSections), - ParseCabalFields (..), - ParseCabalFile (..)) -import qualified Ide.Plugin.Cabal.Completion.Types as Types -import Ide.Plugin.Cabal.Definition (gotoDefinition) -import qualified Ide.Plugin.Cabal.Diagnostics as Diagnostics -import qualified Ide.Plugin.Cabal.FieldSuggest as FieldSuggest -import qualified Ide.Plugin.Cabal.LicenseSuggest as LicenseSuggest -import Ide.Plugin.Cabal.Orphans () -import Ide.Plugin.Cabal.Outline -import qualified Ide.Plugin.Cabal.Parse as Parse import Ide.Types import qualified Language.LSP.Protocol.Lens as JL import qualified Language.LSP.Protocol.Message as LSP @@ -91,8 +62,8 @@ import qualified Language.LSP.VFS as VFS import Text.Regex.TDFA -import qualified Data.Text () -import qualified Ide.Plugin.Cabal.CabalAdd as CabalAdd +import qualified Data.Text () +import qualified Ide.Plugin.Cabal.CabalAdd as CabalAdd data Log = LogModificationTime NormalizedFilePath FileVersion @@ -344,34 +315,6 @@ fieldSuggestCodeAction recorder ide _ (CodeActionParams _ _ (TextDocumentIdentif let completionTexts = fmap (^. JL.label) completions pure $ FieldSuggest.fieldErrorAction uri fieldName completionTexts _range --- | CodeActions for going to definitions. --- --- Provides a CodeAction for going to a definition when clicking on an identifier. --- The definition is found by traversing the sections and comparing their name to --- the clicked identifier. --- --- TODO: Support more definitions than sections. -gotoDefinition :: PluginMethodHandler IdeState LSP.Method_TextDocumentDefinition -gotoDefinition ideState _ msgParam = do - nfp <- getNormalizedFilePathE uri - cabalFields <- runActionE "cabal-plugin.commonSections" ideState $ useE ParseCabalFields nfp - case CabalFields.findTextWord cursor cabalFields of - Nothing -> - pure $ InR $ InR Null - Just cursorText -> do - commonSections <- runActionE "cabal-plugin.commonSections" ideState $ useE ParseCabalCommonSections nfp - case find (isSectionArgName cursorText) commonSections of - Nothing -> - pure $ InR $ InR Null - Just commonSection -> do - pure $ InL $ Definition $ InL $ Location uri $ CabalFields.getFieldLSPRange commonSection - where - cursor = Types.lspPositionToCabalPosition (msgParam ^. JL.position) - uri = msgParam ^. JL.textDocument . JL.uri - isSectionArgName name (Syntax.Section _ sectionArgName _) = name == CabalFields.onelineSectionArgs sectionArgName - isSectionArgName _ _ = False - - cabalAddCodeAction :: PluginMethodHandler IdeState 'LSP.Method_TextDocumentCodeAction cabalAddCodeAction state plId (CodeActionParams _ _ (TextDocumentIdentifier uri) _ CodeActionContext{_diagnostics=diags}) = do maxCompls <- fmap maxCompletions . liftIO $ runAction "cabal.cabal-add" state getClientConfigAction diff --git a/plugins/hls-cabal-plugin/test/Main.hs b/plugins/hls-cabal-plugin/test/Main.hs index 245c7614a8..499d4aa569 100644 --- a/plugins/hls-cabal-plugin/test/Main.hs +++ b/plugins/hls-cabal-plugin/test/Main.hs @@ -232,59 +232,6 @@ codeActionTests = testGroup "Code Actions" guard (_title == "Replace with " <> license) pure action --- ---------------------------------------------------------------------------- --- Goto Definition Tests --- ---------------------------------------------------------------------------- - -gotoDefinitionTests :: TestTree -gotoDefinitionTests = testGroup "Goto Definition" - [ positiveTest "middle of identifier" (mkP 27 16) (mkR 6 0 7 22) - , positiveTest "left of identifier" (mkP 30 12) (mkR 10 0 17 40) - , positiveTest "right of identifier" (mkP 33 22) (mkR 20 0 23 34) - , positiveTest "left of '-' in identifier" (mkP 36 20) (mkR 6 0 7 22) - , positiveTest "right of '-' in identifier" (mkP 39 19) (mkR 10 0 17 40) - , positiveTest "identifier in identifier list" (mkP 42 16) (mkR 20 0 23 34) - , positiveTest "left of ',' right of identifier" (mkP 45 33) (mkR 10 0 17 40) - , positiveTest "right of ',' left of identifier" (mkP 48 34) (mkR 6 0 7 22) - - , negativeTest "right of ',' left of space" (mkP 51 23) - , negativeTest "right of ':' left of space" (mkP 54 11) - , negativeTest "not a definition" (mkP 57 8) - , negativeTest "empty space" (mkP 59 7) - ] - where - mkP :: UInt -> UInt -> Position - mkP x1 y1 = Position x1 y1 - - mkR :: UInt -> UInt -> UInt -> UInt -> Range - mkR x1 y1 x2 y2 = Range (mkP x1 y1) (mkP x2 y2) - - getDefinition :: Show b => (Definition |? b) -> Range - getDefinition (InL (Definition (InL loc))) = loc^.L.range - getDefinition unk = error $ "Unexpected pattern '" ++ show unk ++ "' , expected '(InL (Definition (InL loc))'" - - -- A positive test checks if the provided range is equal - -- to the expected range from the definition in the test file. - -- The test emulates a goto-definition request of an actual definition. - positiveTest :: TestName -> Position -> Range -> TestTree - positiveTest testName cursorPos expectedRange = - runCabalTestCaseSession testName "goto-definition" $ do - doc <- openDoc "simple-with-common.cabal" "cabal" - definitions <- getDefinitions doc cursorPos - let locationRange = getDefinition definitions - liftIO $ locationRange @?= expectedRange - - -- A negative test checks if the request failed and - -- the provided result is empty, i.e. `InR $ InR Null`. - -- The test emulates a goto-definition request of anything but an - -- actual definition. - negativeTest :: TestName -> Position -> TestTree - negativeTest testName cursorPos = - runCabalTestCaseSession testName "goto-definition" $ do - doc <- openDoc "simple-with-common.cabal" "cabal" - empty <- getDefinitions doc cursorPos - liftIO $ empty @?= (InR $ InR LSP.Null) - -- ---------------------------------------------------------------------------- -- Hover Tests -- ---------------------------------------------------------------------------- From d0400a72be893b1aa03cea8b3076adc47d7a17ad Mon Sep 17 00:00:00 2001 From: George Gerasev Date: Thu, 22 Aug 2024 22:35:18 +0300 Subject: [PATCH 13/16] runActionE -> runAction --- .../hls-cabal-plugin/src/Ide/Plugin/Cabal.hs | 32 +++++++++++-------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs index 8030e89251..c9be870fde 100644 --- a/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs +++ b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs @@ -348,19 +348,25 @@ cabalAddCodeAction state plId (CodeActionParams _ _ (TextDocumentIdentifier uri) hover :: PluginMethodHandler IdeState LSP.Method_TextDocumentHover hover ide _ msgParam = do nfp <- getNormalizedFilePathE uri - (cabalFields, _) <- runActionE "cabal.cabal-hover" ide $ useWithStaleE ParseCabalFields nfp - case CabalFields.findTextWord cursor cabalFields of - Nothing -> - pure $ InR Null - Just cursorText -> do - (gpd, _) <- runActionE "cabal.GPD" ide $ useWithStaleE ParseCabalFile nfp - let depsNames = map dependencyName $ allBuildDepends $ flattenPackageDescription gpd - case filterVersion cursorText of - Nothing -> pure $ InR Null - Just txt -> - if txt `elem` depsNames - then pure $ foundHover (Nothing, [txt <> "\n", documentationText txt]) - else pure $ InR Null + mFields <- liftIO $ runAction "cabal.cabal-hover" ide $ useWithStale ParseCabalFields nfp + case mFields of + Nothing -> pure $ InR Null + Just (cabalFields, _) -> + case CabalFields.findTextWord cursor cabalFields of + Nothing -> + pure $ InR Null + Just cursorText -> do + mGPD <- liftIO $ runAction "cabal.GPD" ide $ useWithStale ParseCabalFile nfp + case mGPD of + Nothing -> pure $ InR Null + Just (gpd, _) -> do + let depsNames = map dependencyName $ allBuildDepends $ flattenPackageDescription gpd + case filterVersion cursorText of + Nothing -> pure $ InR Null + Just txt -> + if txt `elem` depsNames + then pure $ foundHover (Nothing, [txt <> "\n", documentationText txt]) + else pure $ InR Null where cursor = Types.lspPositionToCabalPosition (msgParam ^. JL.position) uri = msgParam ^. JL.textDocument . JL.uri From 0180e7c41798e1c398e74c2efd31e34578fe6f68 Mon Sep 17 00:00:00 2001 From: George Gerasev Date: Fri, 23 Aug 2024 17:21:00 +0300 Subject: [PATCH 14/16] revert prev, useWithStaleE -> useE --- .../hls-cabal-plugin/src/Ide/Plugin/Cabal.hs | 32 ++++++++----------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs index c9be870fde..9fc37ae81b 100644 --- a/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs +++ b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs @@ -348,25 +348,19 @@ cabalAddCodeAction state plId (CodeActionParams _ _ (TextDocumentIdentifier uri) hover :: PluginMethodHandler IdeState LSP.Method_TextDocumentHover hover ide _ msgParam = do nfp <- getNormalizedFilePathE uri - mFields <- liftIO $ runAction "cabal.cabal-hover" ide $ useWithStale ParseCabalFields nfp - case mFields of - Nothing -> pure $ InR Null - Just (cabalFields, _) -> - case CabalFields.findTextWord cursor cabalFields of - Nothing -> - pure $ InR Null - Just cursorText -> do - mGPD <- liftIO $ runAction "cabal.GPD" ide $ useWithStale ParseCabalFile nfp - case mGPD of - Nothing -> pure $ InR Null - Just (gpd, _) -> do - let depsNames = map dependencyName $ allBuildDepends $ flattenPackageDescription gpd - case filterVersion cursorText of - Nothing -> pure $ InR Null - Just txt -> - if txt `elem` depsNames - then pure $ foundHover (Nothing, [txt <> "\n", documentationText txt]) - else pure $ InR Null + cabalFields <- runActionE "cabal.cabal-hover" ide $ useE ParseCabalFields nfp + case CabalFields.findTextWord cursor cabalFields of + Nothing -> + pure $ InR Null + Just cursorText -> do + gpd <- runActionE "cabal.GPD" ide $ useE ParseCabalFile nfp + let depsNames = map dependencyName $ allBuildDepends $ flattenPackageDescription gpd + case filterVersion cursorText of + Nothing -> pure $ InR Null + Just txt -> + if txt `elem` depsNames + then pure $ foundHover (Nothing, [txt <> "\n", documentationText txt]) + else pure $ InR Null where cursor = Types.lspPositionToCabalPosition (msgParam ^. JL.position) uri = msgParam ^. JL.textDocument . JL.uri From d52690d91a6ed5a798644d30084621e1bbbf8967 Mon Sep 17 00:00:00 2001 From: Georgii Gerasev <54953043+VenInf@users.noreply.github.com> Date: Mon, 26 Aug 2024 18:03:21 +0300 Subject: [PATCH 15/16] Update plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs Co-authored-by: fendor --- plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs index 9fc37ae81b..fa76f124bb 100644 --- a/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs +++ b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs @@ -375,7 +375,6 @@ hover ide _ msgParam = do regex = "([a-zA-Z0-9-]*[a-zA-Z0-9])" getMatch :: (T.Text, T.Text, T.Text, [T.Text]) -> Maybe T.Text - getMatch (_, _, _, []) = Nothing getMatch (_, _, _, [dependency]) = Just dependency getMatch (_, _, _, _) = Nothing -- impossible case From f202d4cda14366ad3267e5fa550139eab41f50f2 Mon Sep 17 00:00:00 2001 From: George Gerasev Date: Thu, 29 Aug 2024 22:05:58 +0300 Subject: [PATCH 16/16] + documentation --- plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs index fa76f124bb..fdde678845 100644 --- a/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs +++ b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs @@ -368,6 +368,14 @@ hover ide _ msgParam = do dependencyName :: Dependency -> T.Text dependencyName dep = T.pack $ unPackageName $ depPkgName dep + -- | Removes version requirements like + -- `==1.0.0.0`, `>= 2.1.1` that could be included in + -- hover message. Assumes that the dependency consists + -- of alphanums with dashes in between. Ends with an alphanum. + -- + -- Examples: + -- >>> filterVersion "imp-deps>=2.1.1" + -- "imp-deps" filterVersion :: T.Text -> Maybe T.Text filterVersion msg = getMatch (msg =~ regex) where