|
21 | 21 | -- [1] - https://github.com/jyp/dante
|
22 | 22 | module Ide.Plugin.Eval where
|
23 | 23 |
|
24 |
| -import Control.Arrow (second) |
25 |
| -import qualified Control.Exception as E |
26 |
| -import Control.DeepSeq ( NFData |
27 |
| - , deepseq |
28 |
| - ) |
29 |
| -import Control.Monad (void) |
30 |
| -import Control.Monad.IO.Class (MonadIO (liftIO)) |
31 |
| -import Control.Monad.Trans.Class (MonadTrans (lift)) |
32 |
| -import Control.Monad.Trans.Except (ExceptT (..), runExceptT, |
33 |
| - throwE) |
34 |
| -import Data.Aeson (FromJSON, ToJSON, Value (Null), |
35 |
| - toJSON) |
36 |
| -import Data.Bifunctor (Bifunctor (first)) |
37 |
| -import Data.Char (isSpace) |
38 |
| -import qualified Data.HashMap.Strict as Map |
39 |
| -import Data.Maybe (catMaybes) |
40 |
| -import Data.String (IsString (fromString)) |
41 |
| -import Data.Text (Text) |
42 |
| -import qualified Data.Text as T |
43 |
| -import Data.Time (getCurrentTime) |
| 24 | +import Control.Applicative (Alternative ((<|>))) |
| 25 | +import Control.Arrow (second) |
| 26 | +import Control.DeepSeq (NFData, deepseq) |
| 27 | +import qualified Control.Exception as E |
| 28 | +import Control.Monad (void) |
| 29 | +import Control.Monad.IO.Class (MonadIO (liftIO)) |
| 30 | +import Control.Monad.Trans.Class (MonadTrans (lift)) |
| 31 | +import Control.Monad.Trans.Except (ExceptT (..), runExceptT, throwE) |
| 32 | +import Data.Aeson (FromJSON, ToJSON, Value (Null), |
| 33 | + toJSON) |
| 34 | +import Data.Bifunctor (Bifunctor (first)) |
| 35 | +import Data.Char (isSpace) |
| 36 | +import qualified Data.HashMap.Strict as Map |
| 37 | +import Data.List (find) |
| 38 | +import Data.Maybe (catMaybes) |
| 39 | +import Data.String (IsString (fromString)) |
| 40 | +import Data.Text (Text) |
| 41 | +import qualified Data.Text as T |
| 42 | +import Data.Time (getCurrentTime) |
44 | 43 | import Development.IDE
|
45 |
| -import DynamicLoading (initializePlugins) |
46 |
| -import DynFlags (targetPlatform) |
47 |
| -import Development.IDE.GHC.Compat (Ghc, TcRnExprMode(..), DynFlags, ExecResult (..), GeneralFlag (Opt_IgnoreHpcChanges, Opt_IgnoreOptimChanges, Opt_ImplicitImportQualified), |
48 |
| - GhcLink (LinkInMemory), |
49 |
| - GhcMode (CompManager), |
50 |
| - HscTarget (HscInterpreted), |
51 |
| - LoadHowMuch (LoadAllTargets), |
52 |
| - SuccessFlag (..), |
53 |
| - execLineNumber, execOptions, |
54 |
| - execSourceFile, execStmt, |
55 |
| - exprType, |
56 |
| - getContext, |
57 |
| - getInteractiveDynFlags, |
58 |
| - getSession, getSessionDynFlags, |
59 |
| - ghcLink, ghcMode, hscTarget, |
60 |
| - isImport, isStmt, load, |
61 |
| - moduleName, packageFlags, |
62 |
| - parseImportDecl, pkgDatabase, |
63 |
| - pkgState, runDecls, setContext, |
64 |
| - setInteractiveDynFlags, |
65 |
| - setLogAction, |
66 |
| - setSessionDynFlags, setTargets, |
67 |
| - simpleImportDecl, typeKind, ways) |
68 |
| -import GHC.Generics (Generic) |
69 |
| -import GhcMonad (modifySession) |
70 |
| -import GhcPlugins (defaultLogActionHPutStrDoc, |
71 |
| - gopt_set, gopt_unset, |
72 |
| - interpWays, updateWays, |
73 |
| - wayGeneralFlags, |
74 |
| - wayUnsetGeneralFlags) |
| 44 | +import Development.IDE.GHC.Compat (DynFlags, ExecResult (..), GeneralFlag (Opt_IgnoreHpcChanges, Opt_IgnoreOptimChanges, Opt_ImplicitImportQualified), |
| 45 | + Ghc, GhcLink (LinkInMemory), |
| 46 | + GhcMode (CompManager), |
| 47 | + HscTarget (HscInterpreted), |
| 48 | + LoadHowMuch (LoadAllTargets), |
| 49 | + SuccessFlag (..), |
| 50 | + TcRnExprMode (..), execLineNumber, |
| 51 | + execOptions, execSourceFile, |
| 52 | + execStmt, exprType, getContext, |
| 53 | + getInteractiveDynFlags, getSession, |
| 54 | + getSessionDynFlags, ghcLink, |
| 55 | + ghcMode, hscTarget, isImport, |
| 56 | + isStmt, load, moduleName, |
| 57 | + packageFlags, parseImportDecl, |
| 58 | + pkgDatabase, pkgState, runDecls, |
| 59 | + setContext, setInteractiveDynFlags, |
| 60 | + setLogAction, setSessionDynFlags, |
| 61 | + setTargets, simpleImportDecl, |
| 62 | + typeKind, ways) |
| 63 | +import DynamicLoading (initializePlugins) |
| 64 | +import DynFlags (targetPlatform) |
| 65 | +import GHC.Generics (Generic) |
| 66 | +import GhcMonad (modifySession) |
| 67 | +import GhcPlugins (defaultLogActionHPutStrDoc, |
| 68 | + gopt_set, gopt_unset, interpWays, |
| 69 | + updateWays, wayGeneralFlags, |
| 70 | + wayUnsetGeneralFlags) |
75 | 71 | import HscTypes
|
76 | 72 | import Ide.Plugin
|
77 | 73 | import Ide.Types
|
78 | 74 | import Language.Haskell.LSP.Core
|
79 | 75 | import Language.Haskell.LSP.Types
|
80 |
| -import Language.Haskell.LSP.VFS (virtualFileText) |
81 |
| -import Outputable (ppr, showSDoc) |
82 |
| -import PrelNames (pRELUDE) |
| 76 | +import Language.Haskell.LSP.VFS (virtualFileText) |
| 77 | +import Outputable (nest, ppr, showSDoc, text, ($$), |
| 78 | + (<+>)) |
| 79 | +import PrelNames (pRELUDE) |
83 | 80 | import System.FilePath
|
84 |
| -import System.IO (hClose) |
| 81 | +import System.IO (hClose) |
85 | 82 | import System.IO.Temp
|
86 |
| -import Type.Reflection (Typeable) |
| 83 | +import Type.Reflection (Typeable) |
87 | 84 |
|
88 | 85 | descriptor :: PluginId -> PluginDescriptor
|
89 | 86 | descriptor plId =
|
@@ -280,38 +277,66 @@ runEvalCmd lsp state EvalParams {..} = withIndefiniteProgress lsp "Eval" Cancell
|
280 | 277 |
|
281 | 278 | return (WorkspaceApplyEdit, ApplyWorkspaceEditParams workspaceEdits)
|
282 | 279 |
|
| 280 | +-- | Resulting @Text@ MUST NOT prefix each line with @--@ |
| 281 | +-- Such comment-related post-process will be taken place |
| 282 | +-- solely in 'evalGhciLikeCmd'. |
| 283 | +type GHCiLikeCmd = DynFlags -> Text -> Ghc (Maybe Text) |
| 284 | + |
| 285 | +-- Should we use some sort of trie here? |
| 286 | +ghciLikeCommands :: [(Text, GHCiLikeCmd)] |
| 287 | +ghciLikeCommands = |
| 288 | + [ ("kind", doKindCmd False) |
| 289 | + , ("kind!", doKindCmd True) |
| 290 | + , ("type", doTypeCmd) |
| 291 | + ] |
| 292 | + |
283 | 293 | evalGhciLikeCmd :: Text -> Text -> Ghc (Maybe Text)
|
284 | 294 | evalGhciLikeCmd cmd arg = do
|
285 | 295 | df <- getSessionDynFlags
|
286 |
| - let tppr = T.pack . showSDoc df . ppr |
287 |
| - case cmd of |
288 |
| - "kind" -> do |
289 |
| - let input = T.strip arg |
290 |
| - (_, kind) <- typeKind False $ T.unpack input |
291 |
| - pure $ Just $ "-- " <> input <> " :: " <> tppr kind <> "\n" |
292 |
| - "kind!" -> do |
293 |
| - let input = T.strip arg |
294 |
| - (ty, kind) <- typeKind True $ T.unpack input |
295 |
| - pure |
296 |
| - $ Just |
297 |
| - $ T.unlines |
298 |
| - $ map ("-- " <>) |
299 |
| - [ input <> " :: " <> tppr kind |
300 |
| - , "= " <> tppr ty |
301 |
| - ] |
302 |
| - "type" -> do |
303 |
| - let (emod, expr) = parseExprMode arg |
304 |
| - ty <- exprType emod $ T.unpack expr |
305 |
| - pure $ Just $ |
306 |
| - "-- " <> expr <> " :: " <> tppr ty <> "\n" |
307 |
| - _ -> E.throw $ GhciLikeCmdNotImplemented cmd arg |
| 296 | + case lookup cmd ghciLikeCommands |
| 297 | + <|> snd <$> find (T.isPrefixOf cmd . fst) ghciLikeCommands of |
| 298 | + Just hndler -> |
| 299 | + fmap |
| 300 | + (T.unlines . map ("-- " <>) . T.lines |
| 301 | + ) |
| 302 | + <$> hndler df arg |
| 303 | + _ -> E.throw $ GhciLikeCmdNotImplemented cmd arg |
| 304 | + |
| 305 | +doKindCmd :: Bool -> DynFlags -> Text -> Ghc (Maybe Text) |
| 306 | +doKindCmd False df arg = do |
| 307 | + let input = T.strip arg |
| 308 | + (_, kind) <- typeKind False $ T.unpack input |
| 309 | + let kindText = text (T.unpack input) <+> "::" <+> ppr kind |
| 310 | + pure $ Just $ T.pack (showSDoc df kindText) |
| 311 | +doKindCmd True df arg = do |
| 312 | + let input = T.strip arg |
| 313 | + (ty, kind) <- typeKind True $ T.unpack input |
| 314 | + let kindDoc = text (T.unpack input) <+> "::" <+> ppr kind |
| 315 | + tyDoc = "=" <+> ppr ty |
| 316 | + pure $ Just $ T.pack (showSDoc df $ kindDoc $$ tyDoc) |
| 317 | + |
| 318 | +doTypeCmd :: DynFlags -> Text -> Ghc (Maybe Text) |
| 319 | +doTypeCmd dflags arg = do |
| 320 | + let (emod, expr) = parseExprMode arg |
| 321 | + ty <- exprType emod $ T.unpack expr |
| 322 | + let rawType = T.strip $ T.pack $ showSDoc dflags $ ppr ty |
| 323 | + broken = T.any (\c -> c == '\r' || c == '\n') rawType |
| 324 | + pure $ Just $ |
| 325 | + if broken |
| 326 | + then T.pack |
| 327 | + $ showSDoc dflags |
| 328 | + $ text (T.unpack expr) $$ |
| 329 | + (nest 2 $ |
| 330 | + "::" <+> ppr ty |
| 331 | + ) |
| 332 | + else expr <> " :: " <> rawType <> "\n" |
308 | 333 |
|
309 | 334 | parseExprMode :: Text -> (TcRnExprMode, T.Text)
|
310 | 335 | parseExprMode rawArg =
|
311 | 336 | case T.break isSpace rawArg of
|
312 | 337 | ("+v", rest) -> (TM_NoInst, T.strip rest)
|
313 | 338 | ("+d", rest) -> (TM_Default, T.strip rest)
|
314 |
| - _ -> (TM_Inst, rawArg) |
| 339 | + _ -> (TM_Inst, rawArg) |
315 | 340 |
|
316 | 341 | data GhciLikeCmdException =
|
317 | 342 | GhciLikeCmdNotImplemented
|
|
0 commit comments