Skip to content

Commit 0c5a317

Browse files
authored
Emit holes as diagnostics (#1653)
* ExactPrint with annotations * Implement getAllHoles method * setup hole diagnostics * Get hole diagnostics working * Use config in the hole rule * Chop out old ideas * Tidying * Not sure where this discrepancy comes from, but yolo * Try bumping bounds to 8.10 * ahhhh * Add getDiagnosticsAction * Maybe better way of getting the severity? * Put property support in ghcide * Minor tidying * Hlint + bad merge double whammy of annoyingness * Remove getDiagnosticsAction
1 parent e463dc6 commit 0c5a317

File tree

4 files changed

+99
-4
lines changed

4 files changed

+99
-4
lines changed

ghcide/src/Development/IDE/Core/Rules.hs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ module Development.IDE.Core.Rules(
2727
getParsedModule,
2828
getParsedModuleWithComments,
2929
getClientConfigAction,
30+
usePropertyAction,
3031
-- * Rules
3132
CompiledLinkables(..),
3233
IsHiFileStable(..),
@@ -139,7 +140,12 @@ import Language.LSP.Types (SMethod (SCustomM
139140
import Language.LSP.VFS
140141
import Module
141142
import TcRnMonad (tcg_dependent_files)
142-
import Control.Applicative
143+
144+
import Ide.Plugin.Properties (HasProperty, KeyNameProxy, Properties, ToHsType, useProperty)
145+
import Ide.Types (PluginId)
146+
import Data.Default (def)
147+
import Ide.PluginUtils (configForPlugin)
148+
import Control.Applicative
143149

144150
-- | This is useful for rules to convert rules that can only produce errors or
145151
-- a result into the more general IdeResult type that supports producing
@@ -940,6 +946,19 @@ getClientConfigAction defValue = do
940946
Just (Success c) -> return c
941947
_ -> return defValue
942948

949+
usePropertyAction ::
950+
(HasProperty s k t r) =>
951+
KeyNameProxy s ->
952+
PluginId ->
953+
Properties r ->
954+
Action (ToHsType t)
955+
usePropertyAction kn plId p = do
956+
config <- getClientConfigAction def
957+
let pluginConfig = configForPlugin config plId
958+
pure $ useProperty kn p $ plcConfig pluginConfig
959+
960+
-- ---------------------------------------------------------------------
961+
943962
-- | For now we always use bytecode unless something uses unboxed sums and tuples along with TH
944963
getLinkableType :: NormalizedFilePath -> Action (Maybe LinkableType)
945964
getLinkableType f = use_ NeedsCompilation f

plugins/hls-tactics-plugin/hls-tactics-plugin.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ library
8181
, text
8282
, transformers
8383
, deepseq
84+
, unordered-containers
8485

8586
default-language: Haskell2010
8687
default-extensions:

plugins/hls-tactics-plugin/src/Wingman/LanguageServer.hs

Lines changed: 77 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{-# LANGUAGE CPP #-}
22
{-# LANGUAGE OverloadedStrings #-}
3+
{-# LANGUAGE TypeFamilies #-}
34

45
module Wingman.LanguageServer where
56

@@ -12,25 +13,30 @@ import Data.Coerce
1213
import Data.Functor ((<&>))
1314
import Data.Generics.Aliases (mkQ)
1415
import Data.Generics.Schemes (everything)
16+
import qualified Data.HashMap.Strict as Map
1517
import Data.IORef (readIORef)
1618
import qualified Data.Map as M
1719
import Data.Maybe
1820
import Data.Monoid
1921
import qualified Data.Set as S
2022
import qualified Data.Text as T
2123
import Data.Traversable
24+
import Development.IDE (getFilesOfInterest, ShowDiagnostic (ShowDiag), srcSpanToRange)
2225
import Development.IDE (hscEnv)
26+
import Development.IDE.Core.PositionMapping
2327
import Development.IDE.Core.RuleTypes
28+
import Development.IDE.Core.Rules (usePropertyAction)
2429
import Development.IDE.Core.Service (runAction)
25-
import Development.IDE.Core.Shake (IdeState (..), use)
30+
import Development.IDE.Core.Shake (IdeState (..), uses, define, use)
2631
import qualified Development.IDE.Core.Shake as IDE
2732
import Development.IDE.Core.UseStale
2833
import Development.IDE.GHC.Compat
2934
import Development.IDE.GHC.Error (realSrcSpanToRange)
3035
import Development.IDE.Spans.LocalBindings (Bindings, getDefiningBindings)
31-
import Development.Shake (Action, RuleResult)
36+
import Development.Shake (Action, RuleResult, Rules, action)
3237
import Development.Shake.Classes (Typeable, Binary, Hashable, NFData)
3338
import qualified FastString
39+
import GHC.Generics (Generic)
3440
import GhcPlugins (tupleDataCon, consDataCon, substTyAddInScope, ExternalPackageState, HscEnv (hsc_EPS), liftIO)
3541
import qualified Ide.Plugin.Config as Plugin
3642
import Ide.Plugin.Properties
@@ -109,7 +115,8 @@ unsafeRunStaleIde state nfp a = do
109115
------------------------------------------------------------------------------
110116

111117
properties :: Properties
112-
'[ 'PropertyKey "max_use_ctor_actions" 'TInteger
118+
'[ 'PropertyKey "hole_severity" ('TEnum (Maybe DiagnosticSeverity))
119+
, 'PropertyKey "max_use_ctor_actions" 'TInteger
113120
, 'PropertyKey "features" 'TString
114121
, 'PropertyKey "timeout_duration" 'TInteger
115122
]
@@ -120,6 +127,15 @@ properties = emptyProperties
120127
"Feature set used by Wingman" ""
121128
& defineIntegerProperty #max_use_ctor_actions
122129
"Maximum number of `Use constructor <x>` code actions that can appear" 5
130+
& defineEnumProperty #hole_severity
131+
"The severity to use when showing hole diagnostics. These are noisy, but some editors don't allow jumping to all severities."
132+
[ (Just DsError, "error")
133+
, (Just DsWarning, "warning")
134+
, (Just DsInfo, "info")
135+
, (Just DsHint, "hint")
136+
, (Nothing, "none")
137+
]
138+
Nothing
123139

124140

125141
-- | Get the the plugin config
@@ -421,3 +437,61 @@ mkShowMessageParams ufm = ShowMessageParams (ufmSeverity ufm) $ T.pack $ show uf
421437
showLspMessage :: MonadLsp cfg m => ShowMessageParams -> m ()
422438
showLspMessage = sendNotification SWindowShowMessage
423439

440+
441+
-- This rule only exists for generating file diagnostics
442+
-- so the RuleResult is empty
443+
data WriteDiagnostics = WriteDiagnostics
444+
deriving (Eq, Show, Typeable, Generic)
445+
446+
instance Hashable WriteDiagnostics
447+
instance NFData WriteDiagnostics
448+
instance Binary WriteDiagnostics
449+
450+
type instance RuleResult WriteDiagnostics = ()
451+
452+
wingmanRules :: PluginId -> Rules ()
453+
wingmanRules plId = do
454+
define $ \WriteDiagnostics nfp ->
455+
usePropertyAction #hole_severity plId properties >>= \case
456+
Nothing -> pure (mempty, Just ())
457+
Just severity ->
458+
use GetParsedModule nfp >>= \case
459+
Nothing ->
460+
pure ([], Nothing)
461+
Just pm -> do
462+
let holes :: [Range]
463+
holes =
464+
everything (<>)
465+
(mkQ mempty $ \case
466+
L span (HsVar _ (L _ name))
467+
| isHole (occName name) ->
468+
maybeToList $ srcSpanToRange span
469+
L span (HsUnboundVar _ (TrueExprHole occ))
470+
| isHole occ ->
471+
maybeToList $ srcSpanToRange span
472+
#if __GLASGOW_HASKELL__ <= 808
473+
L span (EWildPat _) ->
474+
maybeToList $ srcSpanToRange span
475+
#endif
476+
(_ :: LHsExpr GhcPs) -> mempty
477+
) $ pm_parsed_source pm
478+
pure
479+
( fmap (\r -> (nfp, ShowDiag, mkDiagnostic severity r)) holes
480+
, Just ()
481+
)
482+
483+
action $ do
484+
files <- getFilesOfInterest
485+
void $ uses WriteDiagnostics $ Map.keys files
486+
487+
488+
mkDiagnostic :: DiagnosticSeverity -> Range -> Diagnostic
489+
mkDiagnostic severity r =
490+
Diagnostic r
491+
(Just severity)
492+
(Just $ InR "hole")
493+
(Just "wingman")
494+
"Hole"
495+
(Just $ List [DtUnnecessary])
496+
Nothing
497+

plugins/hls-tactics-plugin/src/Wingman/Plugin.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ descriptor plId = (defaultPluginDescriptor plId)
4949
[minBound .. maxBound]
5050
, pluginHandlers =
5151
mkPluginHandler STextDocumentCodeAction codeActionProvider
52+
, pluginRules = wingmanRules plId
5253
, pluginCustomConfig =
5354
mkCustomConfig properties
5455
}

0 commit comments

Comments
 (0)