Skip to content

Commit 1c74d0c

Browse files
authored
Merge branch 'master' into inlay-hints-record-wildcards
2 parents d786549 + 2253752 commit 1c74d0c

32 files changed

+963
-14
lines changed

.github/actions/setup-build/action.yml

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ runs:
3131
sudo chown -R $USER /usr/local/.ghcup
3232
shell: bash
3333

34-
- uses: haskell-actions/[email protected].5
34+
- uses: haskell-actions/[email protected].6
3535
id: HaskEnvSetup
3636
with:
3737
ghc-version : ${{ inputs.ghc }}
@@ -116,3 +116,18 @@ runs:
116116
- name: "Remove freeze file"
117117
run: rm -f cabal.project.freeze
118118
shell: bash
119+
120+
# Make sure to clear all unneeded `ghcup`` caches.
121+
# At some point, we were running out of disk space, see issue
122+
# https://github.com/haskell/haskell-language-server/issues/4386 for details.
123+
#
124+
# Using "printf" debugging (`du -sh *` and `df -h /`) and binary searching,
125+
# we figured out that `ghcup` caches are taking up a sizable portion of the
126+
# disk space.
127+
# Thus, we remove anything we don't need, especially caches and temporary files.
128+
# For got measure, we also make sure no other tooling versions are
129+
# installed besides the ones we explicitly want.
130+
- name: "Remove ghcup caches"
131+
if: runner.os == 'Linux'
132+
run: ghcup gc --ghc-old --share-dir --hls-no-ghc --cache --tmpdirs --unset
133+
shell: bash

.github/workflows/bench.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ jobs:
127127
example: ['cabal', 'lsp-types']
128128

129129
steps:
130-
- uses: haskell-actions/[email protected].3
130+
- uses: haskell-actions/[email protected].6
131131
with:
132132
ghc-version : ${{ matrix.ghc }}
133133
cabal-version: ${{ matrix.cabal }}

cabal.project

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@ packages:
77
./hls-plugin-api
88
./hls-test-utils
99

10+
-- Only keep this until https://github.com/Bodigrim/cabal-add/issues/7
11+
-- is resolved
12+
source-repository-package
13+
type: git
14+
location: https://github.com/Bodigrim/cabal-add.git
15+
tag: 8c004e2a4329232f9824425f5472b2d6d7958bbd
16+
1017
index-state: 2024-06-29T00:00:00Z
1118

1219
tests: True

exe/Wrapper.hs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ module Main where
99

1010
import Control.Monad.Extra
1111
import Data.Default
12-
import Data.Either.Extra (eitherToMaybe)
1312
import Data.Foldable
1413
import Data.List
1514
import Data.List.Extra (trimEnd)
@@ -76,8 +75,11 @@ main = do
7675
putStrLn $ showProgramVersionOfInterest programsOfInterest
7776
putStrLn "Tool versions in your project"
7877
cradle <- findProjectCradle' recorder False
79-
ghcVersion <- runExceptT $ getRuntimeGhcVersion' cradle
80-
putStrLn $ showProgramVersion "ghc" $ mkVersion =<< eitherToMaybe ghcVersion
78+
runExceptT (getRuntimeGhcVersion' cradle) >>= \case
79+
Left err ->
80+
T.hPutStrLn stderr (prettyError err NoShorten)
81+
Right ghcVersion ->
82+
putStrLn $ showProgramVersion "ghc" $ mkVersion ghcVersion
8183

8284
VersionMode PrintVersion ->
8385
putStrLn hlsVer

haskell-language-server.cabal

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,7 @@ library hls-cabal-plugin
244244
Ide.Plugin.Cabal.Completion.Types
245245
Ide.Plugin.Cabal.FieldSuggest
246246
Ide.Plugin.Cabal.LicenseSuggest
247+
Ide.Plugin.Cabal.CabalAdd
247248
Ide.Plugin.Cabal.Orphans
248249
Ide.Plugin.Cabal.Outline
249250
Ide.Plugin.Cabal.Parse
@@ -270,6 +271,12 @@ library hls-cabal-plugin
270271
, transformers
271272
, unordered-containers >=0.2.10.0
272273
, containers
274+
, cabal-add
275+
, process
276+
, aeson
277+
, Cabal
278+
, pretty
279+
273280
hs-source-dirs: plugins/hls-cabal-plugin/src
274281

275282
test-suite hls-cabal-plugin-tests
@@ -284,6 +291,7 @@ test-suite hls-cabal-plugin-tests
284291
Context
285292
Utils
286293
Outline
294+
CabalAdd
287295
build-depends:
288296
, base
289297
, bytestring
@@ -296,6 +304,7 @@ test-suite hls-cabal-plugin-tests
296304
, lens
297305
, lsp-types
298306
, text
307+
, hls-plugin-api
299308

300309
-----------------------------
301310
-- class plugin

plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
{-# LANGUAGE OverloadedStrings #-}
55
{-# LANGUAGE TypeFamilies #-}
66

7-
module Ide.Plugin.Cabal (descriptor, Log (..)) where
7+
module Ide.Plugin.Cabal (descriptor, haskellInteractionDescriptor, Log (..)) where
88

99
import Control.Concurrent.Strict
1010
import Control.DeepSeq
@@ -17,12 +17,14 @@ import qualified Data.ByteString as BS
1717
import Data.Hashable
1818
import Data.HashMap.Strict (HashMap)
1919
import qualified Data.HashMap.Strict as HashMap
20+
import Data.List (find)
2021
import qualified Data.List.NonEmpty as NE
2122
import qualified Data.Maybe as Maybe
2223
import qualified Data.Text as T
2324
import qualified Data.Text.Encoding as Encoding
2425
import Data.Typeable
2526
import Development.IDE as D
27+
import Development.IDE.Core.PluginUtils
2628
import Development.IDE.Core.Shake (restartShakeSession)
2729
import qualified Development.IDE.Core.Shake as Shake
2830
import Development.IDE.Graph (Key, alwaysRerun)
@@ -31,6 +33,7 @@ import Development.IDE.Types.Shake (toKey)
3133
import qualified Distribution.Fields as Syntax
3234
import qualified Distribution.Parsec.Position as Syntax
3335
import GHC.Generics
36+
import Ide.Plugin.Cabal.Completion.CabalFields as CabalFields
3437
import qualified Ide.Plugin.Cabal.Completion.Completer.Types as CompleterTypes
3538
import qualified Ide.Plugin.Cabal.Completion.Completions as Completions
3639
import Ide.Plugin.Cabal.Completion.Types (ParseCabalCommonSections (ParseCabalCommonSections),
@@ -43,12 +46,16 @@ import qualified Ide.Plugin.Cabal.LicenseSuggest as LicenseSuggest
4346
import Ide.Plugin.Cabal.Orphans ()
4447
import Ide.Plugin.Cabal.Outline
4548
import qualified Ide.Plugin.Cabal.Parse as Parse
49+
import Ide.Plugin.Error
4650
import Ide.Types
4751
import qualified Language.LSP.Protocol.Lens as JL
4852
import qualified Language.LSP.Protocol.Message as LSP
4953
import Language.LSP.Protocol.Types
5054
import qualified Language.LSP.VFS as VFS
5155

56+
import qualified Data.Text ()
57+
import qualified Ide.Plugin.Cabal.CabalAdd as CabalAdd
58+
5259
data Log
5360
= LogModificationTime NormalizedFilePath FileVersion
5461
| LogShake Shake.Log
@@ -59,6 +66,7 @@ data Log
5966
| LogFOI (HashMap NormalizedFilePath FileOfInterestStatus)
6067
| LogCompletionContext Types.Context Position
6168
| LogCompletions Types.Log
69+
| LogCabalAdd CabalAdd.Log
6270
deriving (Show)
6371

6472
instance Pretty Log where
@@ -82,6 +90,25 @@ instance Pretty Log where
8290
<+> "for cursor position:"
8391
<+> pretty position
8492
LogCompletions logs -> pretty logs
93+
LogCabalAdd logs -> pretty logs
94+
95+
-- | Some actions with cabal files originate from haskell files.
96+
-- This descriptor allows to hook into the diagnostics of haskell source files, and
97+
-- allows us to provide code actions and commands that interact with `.cabal` files.
98+
haskellInteractionDescriptor :: Recorder (WithPriority Log) -> PluginId -> PluginDescriptor IdeState
99+
haskellInteractionDescriptor recorder plId =
100+
(defaultPluginDescriptor plId "Provides the cabal-add code action in haskell files")
101+
{ pluginHandlers =
102+
mconcat
103+
[ mkPluginHandler LSP.SMethod_TextDocumentCodeAction cabalAddCodeAction
104+
]
105+
, pluginCommands = [PluginCommand CabalAdd.cabalAddCommand "add a dependency to a cabal file" (CabalAdd.command cabalAddRecorder)]
106+
, pluginRules = pure ()
107+
, pluginNotificationHandlers = mempty
108+
}
109+
where
110+
cabalAddRecorder = cmapWithPrio LogCabalAdd recorder
111+
85112

86113
descriptor :: Recorder (WithPriority Log) -> PluginId -> PluginDescriptor IdeState
87114
descriptor recorder plId =
@@ -93,6 +120,7 @@ descriptor recorder plId =
93120
, mkPluginHandler LSP.SMethod_TextDocumentCompletion $ completion recorder
94121
, mkPluginHandler LSP.SMethod_TextDocumentDocumentSymbol moduleOutline
95122
, mkPluginHandler LSP.SMethod_TextDocumentCodeAction $ fieldSuggestCodeAction recorder
123+
, mkPluginHandler LSP.SMethod_TextDocumentDefinition gotoDefinition
96124
]
97125
, pluginNotificationHandlers =
98126
mconcat
@@ -277,6 +305,59 @@ fieldSuggestCodeAction recorder ide _ (CodeActionParams _ _ (TextDocumentIdentif
277305
let completionTexts = fmap (^. JL.label) completions
278306
pure $ FieldSuggest.fieldErrorAction uri fieldName completionTexts _range
279307

308+
-- | CodeActions for going to definitions.
309+
--
310+
-- Provides a CodeAction for going to a definition when clicking on an identifier.
311+
-- The definition is found by traversing the sections and comparing their name to
312+
-- the clicked identifier.
313+
--
314+
-- TODO: Support more definitions than sections.
315+
gotoDefinition :: PluginMethodHandler IdeState LSP.Method_TextDocumentDefinition
316+
gotoDefinition ideState _ msgParam = do
317+
nfp <- getNormalizedFilePathE uri
318+
cabalFields <- runActionE "cabal-plugin.commonSections" ideState $ useE ParseCabalFields nfp
319+
case CabalFields.findTextWord cursor cabalFields of
320+
Nothing ->
321+
pure $ InR $ InR Null
322+
Just cursorText -> do
323+
commonSections <- runActionE "cabal-plugin.commonSections" ideState $ useE ParseCabalCommonSections nfp
324+
case find (isSectionArgName cursorText) commonSections of
325+
Nothing ->
326+
pure $ InR $ InR Null
327+
Just commonSection -> do
328+
pure $ InL $ Definition $ InL $ Location uri $ CabalFields.getFieldLSPRange commonSection
329+
where
330+
cursor = Types.lspPositionToCabalPosition (msgParam ^. JL.position)
331+
uri = msgParam ^. JL.textDocument . JL.uri
332+
isSectionArgName name (Syntax.Section _ sectionArgName _) = name == CabalFields.onelineSectionArgs sectionArgName
333+
isSectionArgName _ _ = False
334+
335+
cabalAddCodeAction :: PluginMethodHandler IdeState 'LSP.Method_TextDocumentCodeAction
336+
cabalAddCodeAction state plId (CodeActionParams _ _ (TextDocumentIdentifier uri) _ CodeActionContext{_diagnostics=diags}) = do
337+
maxCompls <- fmap maxCompletions . liftIO $ runAction "cabal.cabal-add" state getClientConfigAction
338+
let suggestions = take maxCompls $ concatMap CabalAdd.hiddenPackageSuggestion diags
339+
case suggestions of
340+
[] -> pure $ InL []
341+
_ ->
342+
case uriToFilePath uri of
343+
Nothing -> pure $ InL []
344+
Just haskellFilePath -> do
345+
mbCabalFile <- liftIO $ CabalAdd.findResponsibleCabalFile haskellFilePath
346+
case mbCabalFile of
347+
Nothing -> pure $ InL []
348+
Just cabalFilePath -> do
349+
verTxtDocId <- lift $ pluginGetVersionedTextDoc $ TextDocumentIdentifier (filePathToUri cabalFilePath)
350+
mbGPD <- liftIO $ runAction "cabal.cabal-add" state $ useWithStale ParseCabalFile $ toNormalizedFilePath cabalFilePath
351+
case mbGPD of
352+
Nothing -> pure $ InL []
353+
Just (gpd, _) -> do
354+
actions <- liftIO $ CabalAdd.addDependencySuggestCodeAction plId verTxtDocId
355+
suggestions
356+
haskellFilePath cabalFilePath
357+
gpd
358+
pure $ InL $ fmap InR actions
359+
360+
280361
-- ----------------------------------------------------------------
281362
-- Cabal file of Interest rules and global variable
282363
-- ----------------------------------------------------------------

0 commit comments

Comments
 (0)