Skip to content

Cabal go to module's definition #4380

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 30 commits into from
Aug 22, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
f3cdcbd
forced PR 4375
VenInf Aug 12, 2024
169fa2c
get modules with names
VenInf Aug 12, 2024
a49ecea
finding hsSourceDirs
VenInf Aug 13, 2024
2ff597a
correct path, indefinite search(?)
VenInf Aug 13, 2024
5cc3906
formatting and docs
VenInf Aug 14, 2024
7cf0265
formatting
VenInf Aug 14, 2024
1236c06
Update plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/Completion/Cabal…
VenInf Aug 16, 2024
adcd5b9
Add Goto Definition for cabal common sections (#4375)
ChristophHochrainer Aug 18, 2024
1bedad2
resolve merging issues
VenInf Aug 18, 2024
bd72add
resolve merging issues
VenInf Aug 18, 2024
3727a63
rm error call
VenInf Aug 18, 2024
b2e45e9
unnecessary parameter
VenInf Aug 18, 2024
766a362
docs
VenInf Aug 18, 2024
8869d6e
first test
VenInf Aug 19, 2024
a9470bd
+ test data
VenInf Aug 19, 2024
d77f879
+ test data
VenInf Aug 19, 2024
f834e47
Definition module
VenInf Aug 19, 2024
901adc0
formattings
VenInf Aug 19, 2024
5d149f4
separate test file
VenInf Aug 19, 2024
5e1a604
more tests
VenInf Aug 19, 2024
660dc88
refactoring and docs
VenInf Aug 20, 2024
13dd3db
docs
VenInf Aug 20, 2024
7f08ee2
grammar
VenInf Aug 20, 2024
306911e
Merge branch 'master' into cabal-go-to-modules-definition
VenInf Aug 20, 2024
adcbe09
rename gotoDefinition to gotoDefinitionAction
VenInf Aug 20, 2024
39b4389
fix merge issues
VenInf Aug 20, 2024
6906a94
Merge branch 'master' into cabal-go-to-modules-definition
VenInf Aug 20, 2024
cec4e54
Apply suggestions from code review
VenInf Aug 21, 2024
dcc045a
docs and small changes
VenInf Aug 21, 2024
25847d5
Merge branch 'master' into cabal-go-to-modules-definition
VenInf Aug 21, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
fix merge issues
  • Loading branch information
VenInf committed Aug 20, 2024
commit 39b43893eec16fbbec4893afafd98f6b94c0ef9a
34 changes: 2 additions & 32 deletions plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,12 @@ 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.Typeable
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)
Expand All @@ -33,21 +31,19 @@ import Development.IDE.Types.Shake (toKey)
import qualified Distribution.Fields as Syntax
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 Ide.Plugin.Cabal.Definition (gotoDefinitionAction)
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.Plugin.Error
import Ide.Types
import qualified Language.LSP.Protocol.Lens as JL
import qualified Language.LSP.Protocol.Message as LSP
Expand Down Expand Up @@ -98,7 +94,7 @@ descriptor recorder plId =
, mkPluginHandler LSP.SMethod_TextDocumentCompletion $ completion recorder
, mkPluginHandler LSP.SMethod_TextDocumentDocumentSymbol moduleOutline
, mkPluginHandler LSP.SMethod_TextDocumentCodeAction $ fieldSuggestCodeAction recorder
, mkPluginHandler LSP.SMethod_TextDocumentDefinition gotoDefinitionAction
, mkPluginHandler LSP.SMethod_TextDocumentDefinition gotoDefinition
]
, pluginNotificationHandlers =
mconcat
Expand Down Expand Up @@ -283,32 +279,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

-- ----------------------------------------------------------------
-- Cabal file of Interest rules and global variable
Expand Down
4 changes: 2 additions & 2 deletions plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/Definition.hs
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ import System.FilePath (joinPath,
-- gathering all possible definitions by calling subfunctions.

-- TODO: Resolve more cases for go-to definition.
gotoDefinitionAction :: PluginMethodHandler IdeState LSP.Method_TextDocumentDefinition
gotoDefinitionAction ide _ msgParam = do
gotoDefinition :: PluginMethodHandler IdeState LSP.Method_TextDocumentDefinition
gotoDefinition ide _ msgParam = do
nfp <- getNormalizedFilePathE uri
cabalFields <- runActionE "cabal-plugin.commonSections" ide $ useE ParseCabalFields nfp
-- Trim the AST tree, so multiple passes in subfunctions won't hurt the performance.
Expand Down
54 changes: 0 additions & 54 deletions plugins/hls-cabal-plugin/test/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import Definition (gotoDefinitionTests)
import Ide.Plugin.Cabal.LicenseSuggest (licenseErrorSuggestion)
import qualified Ide.Plugin.Cabal.Parse as Lib
import qualified Language.LSP.Protocol.Lens as L
import qualified Language.LSP.Protocol.Types as LSP
import Outline (outlineTests)
import System.FilePath
import Test.Hls
Expand Down Expand Up @@ -230,56 +229,3 @@ codeActionTests = testGroup "Code Actions"
InR action@CodeAction{_title} <- codeActions
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)
Loading