From 6169f3050594f8997d6ae45dafe03802602bb16a Mon Sep 17 00:00:00 2001 From: fwcd Date: Tue, 3 Dec 2024 18:33:59 +0100 Subject: [PATCH 01/26] Parse extensions from config --- curry-language-server.cabal | 1 + src/Curry/LanguageServer/Config.hs | 5 ++++ src/Curry/LanguageServer/Extension.hs | 34 +++++++++++++++++++++++++++ 3 files changed, 40 insertions(+) create mode 100644 src/Curry/LanguageServer/Extension.hs diff --git a/curry-language-server.cabal b/curry-language-server.cabal index 628e4c1..ee28bfe 100644 --- a/curry-language-server.cabal +++ b/curry-language-server.cabal @@ -31,6 +31,7 @@ library Curry.LanguageServer.CPM.Deps Curry.LanguageServer.CPM.Monad Curry.LanguageServer.CPM.Process + Curry.LanguageServer.Extension Curry.LanguageServer.FileLoader Curry.LanguageServer.Handlers Curry.LanguageServer.Handlers.Cancel diff --git a/src/Curry/LanguageServer/Config.hs b/src/Curry/LanguageServer/Config.hs index c8cdc7b..8277663 100644 --- a/src/Curry/LanguageServer/Config.hs +++ b/src/Curry/LanguageServer/Config.hs @@ -5,6 +5,7 @@ module Curry.LanguageServer.Config ) where import Colog.Core (Severity (..)) +import Curry.LanguageServer.Extension (Extension (..)) import Data.Aeson ( FromJSON (..) , ToJSON (..) @@ -26,6 +27,7 @@ data Config = Config { forceRecompilation :: Bool , logLevel :: LogLevel , curryPath :: String , useSnippetCompletions :: Bool + , extensions :: [Extension] } deriving (Show, Eq) @@ -36,6 +38,7 @@ instance Default Config where , logLevel = LogLevel Info , curryPath = "pakcs" , useSnippetCompletions = False + , extensions = [] } instance FromJSON Config where @@ -46,6 +49,7 @@ instance FromJSON Config where logLevel <- l .:? "logLevel" .!= (def @Config).logLevel curryPath <- l .:? "curryPath" .!= (def @Config).curryPath useSnippetCompletions <- l .:? "useSnippetCompletions" .!= (def @Config).useSnippetCompletions + extensions <- l .:? "extensions" .!= (def @Config).extensions return Config {..} instance ToJSON Config where @@ -56,6 +60,7 @@ instance ToJSON Config where , "logLevel" .= logLevel , "curryPath" .= curryPath , "useSnippetCompletions" .= useSnippetCompletions + , "extensions" .= extensions ] instance FromJSON LogLevel where diff --git a/src/Curry/LanguageServer/Extension.hs b/src/Curry/LanguageServer/Extension.hs new file mode 100644 index 0000000..edd9036 --- /dev/null +++ b/src/Curry/LanguageServer/Extension.hs @@ -0,0 +1,34 @@ +{-# LANGUAGE DeriveGeneric, OverloadedStrings, TypeApplications #-} +module Curry.LanguageServer.Extension + ( ExtensionPoint (..), Extension (..) + ) where + +import Data.Aeson (FromJSON (..), ToJSON (..)) +import qualified Data.Text as T +import GHC.Generics (Generic (..)) + +data ExtensionPoint = ExtensionPointHover + deriving (Show, Eq) + +data Extension = Extension + { name :: T.Text + , extensionPoint :: ExtensionPoint + , executable :: T.Text + , args :: [T.Text] + } + deriving (Show, Eq, Generic) + +instance FromJSON Extension where + +instance ToJSON Extension where + +instance FromJSON ExtensionPoint where + parseJSON v = do + s <- parseJSON v + return $ case s :: T.Text of + "hover" -> ExtensionPointHover + _ -> error $ "Could not parse extension point " ++ T.unpack s + +instance ToJSON ExtensionPoint where + toJSON p = toJSON @T.Text $ case p of + ExtensionPointHover -> "hover" From c4ed2aa37dd9f7f182dbe7c19688a2019eea15d5 Mon Sep 17 00:00:00 2001 From: fwcd Date: Tue, 3 Dec 2024 19:21:14 +0100 Subject: [PATCH 02/26] Normalize hover contents --- .../Handlers/TextDocument/Hover.hs | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs b/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs index ee80d5d..304cd4b 100644 --- a/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs +++ b/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs @@ -1,4 +1,5 @@ {-# LANGUAGE FlexibleContexts, OverloadedStrings, OverloadedRecordDot, TypeOperators, ViewPatterns #-} +{-# OPTIONS_GHC -Wno-deprecations #-} module Curry.LanguageServer.Handlers.TextDocument.Hover (hoverHandler) where -- Curry Compiler Libraries + Dependencies @@ -66,9 +67,23 @@ typedSpanInfoHover ast@(moduleIdentifier -> mid) pos = do return $ J.Hover contents range previewHover :: J.Hover -> T.Text -previewHover ((^. J.contents) -> J.InL (J.MarkupContent k t)) = case k of J.MarkupKind_Markdown -> markdownToPlain t - J.MarkupKind_PlainText -> t -previewHover _ = "?" +previewHover = T.unlines . (previewMarkupContent <$>) . normalizeContent . (^. J.contents) + +previewMarkupContent :: J.MarkupContent -> T.Text +previewMarkupContent (J.MarkupContent k t) = case k of + J.MarkupKind_Markdown -> markdownToPlain t + J.MarkupKind_PlainText -> t + +normalizeContent :: J.MarkupContent J.|? (J.MarkedString J.|? [J.MarkedString]) -> [J.MarkupContent] +normalizeContent m = case m of + J.InL c -> [c] + J.InR (J.InL s) -> [markedStringToContent s] + J.InR (J.InR ss) -> markedStringToContent <$> ss + +markedStringToContent :: J.MarkedString -> J.MarkupContent +markedStringToContent (J.MarkedString m) = case m of + J.InL t -> J.MarkupContent J.MarkupKind_PlainText t + J.InR (J.MarkedStringWithLanguage l t) -> J.mkMarkdownCodeBlock l t markdownToPlain :: T.Text -> T.Text markdownToPlain t = T.intercalate ", " $ filter includeLine $ T.lines t From 4a052fd8130e8e897e9a53d4e8f10d6d99827607 Mon Sep 17 00:00:00 2001 From: fwcd Date: Tue, 3 Dec 2024 19:34:03 +0100 Subject: [PATCH 03/26] Add mergeHovers utility function --- .../Handlers/TextDocument/Hover.hs | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs b/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs index 304cd4b..b89242c 100644 --- a/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs +++ b/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs @@ -67,15 +67,35 @@ typedSpanInfoHover ast@(moduleIdentifier -> mid) pos = do return $ J.Hover contents range previewHover :: J.Hover -> T.Text -previewHover = T.unlines . (previewMarkupContent <$>) . normalizeContent . (^. J.contents) +previewHover = T.unlines . (previewMarkupContent <$>) . normalizeHoverContents . (^. J.contents) previewMarkupContent :: J.MarkupContent -> T.Text previewMarkupContent (J.MarkupContent k t) = case k of J.MarkupKind_Markdown -> markdownToPlain t J.MarkupKind_PlainText -> t -normalizeContent :: J.MarkupContent J.|? (J.MarkedString J.|? [J.MarkedString]) -> [J.MarkupContent] -normalizeContent m = case m of +mergeHovers :: J.Hover -> J.Hover -> J.Hover +mergeHovers (J.Hover (normalizeHoverContents -> cs1) r1) (J.Hover (normalizeHoverContents -> cs2) r2) = + J.Hover (J.InL (joinMarkupContent (cs1 ++ cs2))) (r1 <|> r2) + +joinMarkupContent :: [J.MarkupContent] -> J.MarkupContent +joinMarkupContent [] = emptyMarkupContent +joinMarkupContent cs = foldr1 mergeMarkupContent cs + +mergeMarkupContent :: J.MarkupContent -> J.MarkupContent -> J.MarkupContent +mergeMarkupContent (normalizeToMarkdown -> J.MarkupContent _ t1) (normalizeToMarkdown -> J.MarkupContent _ t2) = + J.MarkupContent J.MarkupKind_Markdown $ T.unlines [t1, t2] + +emptyMarkupContent :: J.MarkupContent +emptyMarkupContent = J.MarkupContent J.MarkupKind_PlainText "" + +normalizeToMarkdown :: J.MarkupContent -> J.MarkupContent +normalizeToMarkdown (J.MarkupContent k t) = case k of + J.MarkupKind_Markdown -> J.MarkupContent k t + J.MarkupKind_PlainText -> J.mkMarkdownCodeBlock "text" t + +normalizeHoverContents :: J.MarkupContent J.|? (J.MarkedString J.|? [J.MarkedString]) -> [J.MarkupContent] +normalizeHoverContents m = case m of J.InL c -> [c] J.InR (J.InL s) -> [markedStringToContent s] J.InR (J.InR ss) -> markedStringToContent <$> ss From e1172490bd79a01f523638254d4f6e34f7b3be56 Mon Sep 17 00:00:00 2001 From: fwcd Date: Tue, 3 Dec 2024 19:43:02 +0100 Subject: [PATCH 04/26] Stub out extension hovers --- .../Handlers/TextDocument/Hover.hs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs b/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs index b89242c..3829ca3 100644 --- a/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs +++ b/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs @@ -12,6 +12,7 @@ import Control.Monad.Trans.Maybe (MaybeT (..)) import qualified Curry.LanguageServer.Config as CFG import qualified Curry.LanguageServer.Index.Store as I import qualified Curry.LanguageServer.Index.Symbol as I +import Curry.LanguageServer.Extension (ExtensionPoint (..), Extension (..)) import Curry.LanguageServer.Utils.Convert (ppPredTypeToText, currySpanInfo2Range) import Curry.LanguageServer.Index.Resolve (resolveAtPos) import Curry.LanguageServer.Utils.General (liftMaybe) @@ -21,7 +22,7 @@ import Curry.LanguageServer.Utils.Syntax (moduleIdentifier) import Curry.LanguageServer.Utils.Sema (ModuleAST, TypedSpanInfo (..)) import Curry.LanguageServer.Utils.Uri (normalizeUriWithPath) import Curry.LanguageServer.Monad (LSM, getStore) -import Data.Maybe (listToMaybe) +import Data.Maybe (listToMaybe, maybeToList, mapMaybe) import qualified Data.Text as T import qualified Language.LSP.Server as S import qualified Language.LSP.Protocol.Types as J @@ -44,7 +45,10 @@ hoverHandler = S.requestHandler J.SMethod_TextDocumentHover $ \req responder -> fetchHover :: (MonadIO m, MonadLsp CFG.Config m) => I.IndexStore -> I.ModuleStoreEntry -> J.Position -> m (Maybe J.Hover) fetchHover store entry pos = runMaybeT $ do ast <- liftMaybe entry.moduleAST - hover <- liftMaybe $ qualIdentHover store ast pos <|> typedSpanInfoHover ast pos + cfg <- lift S.getConfig + let baseHover = maybeToList $ qualIdentHover store ast pos <|> typedSpanInfoHover ast pos + extHovers = mapMaybe (\ext -> extensionHover ext ast pos) cfg.extensions + hover <- liftMaybe . joinHovers $ baseHover ++ extHovers lift $ infoM $ "Found hover: " <> previewHover hover return hover @@ -66,6 +70,10 @@ typedSpanInfoHover ast@(moduleIdentifier -> mid) pos = do return $ J.Hover contents range +extensionHover :: Extension -> ModuleAST -> J.Position -> Maybe J.Hover +extensionHover e _ _ = case e.extensionPoint of + ExtensionPointHover -> Nothing -- TODO + previewHover :: J.Hover -> T.Text previewHover = T.unlines . (previewMarkupContent <$>) . normalizeHoverContents . (^. J.contents) @@ -74,6 +82,10 @@ previewMarkupContent (J.MarkupContent k t) = case k of J.MarkupKind_Markdown -> markdownToPlain t J.MarkupKind_PlainText -> t +joinHovers :: [J.Hover] -> Maybe J.Hover +joinHovers [] = Nothing +joinHovers hs = Just $ foldr1 mergeHovers hs + mergeHovers :: J.Hover -> J.Hover -> J.Hover mergeHovers (J.Hover (normalizeHoverContents -> cs1) r1) (J.Hover (normalizeHoverContents -> cs2) r2) = J.Hover (J.InL (joinMarkupContent (cs1 ++ cs2))) (r1 <|> r2) From 5a708d5ba36104d4cb49fe63ce89be2d2317b2e5 Mon Sep 17 00:00:00 2001 From: fwcd Date: Tue, 3 Dec 2024 19:43:47 +0100 Subject: [PATCH 05/26] Parse unknown extensions into catch-all ExtensionPoint --- src/Curry/LanguageServer/Extension.hs | 6 ++++-- src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Curry/LanguageServer/Extension.hs b/src/Curry/LanguageServer/Extension.hs index edd9036..80344f0 100644 --- a/src/Curry/LanguageServer/Extension.hs +++ b/src/Curry/LanguageServer/Extension.hs @@ -8,6 +8,7 @@ import qualified Data.Text as T import GHC.Generics (Generic (..)) data ExtensionPoint = ExtensionPointHover + | ExtensionPointUnknown T.Text deriving (Show, Eq) data Extension = Extension @@ -27,8 +28,9 @@ instance FromJSON ExtensionPoint where s <- parseJSON v return $ case s :: T.Text of "hover" -> ExtensionPointHover - _ -> error $ "Could not parse extension point " ++ T.unpack s + _ -> ExtensionPointUnknown s instance ToJSON ExtensionPoint where toJSON p = toJSON @T.Text $ case p of - ExtensionPointHover -> "hover" + ExtensionPointHover -> "hover" + ExtensionPointUnknown s -> s diff --git a/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs b/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs index 3829ca3..adccc0e 100644 --- a/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs +++ b/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs @@ -73,6 +73,7 @@ typedSpanInfoHover ast@(moduleIdentifier -> mid) pos = do extensionHover :: Extension -> ModuleAST -> J.Position -> Maybe J.Hover extensionHover e _ _ = case e.extensionPoint of ExtensionPointHover -> Nothing -- TODO + _ -> Nothing previewHover :: J.Hover -> T.Text previewHover = T.unlines . (previewMarkupContent <$>) . normalizeHoverContents . (^. J.contents) From 72b9b4771c0bf09981d4f0274b0f4a2e4ce0ad90 Mon Sep 17 00:00:00 2001 From: fwcd Date: Tue, 3 Dec 2024 19:46:31 +0100 Subject: [PATCH 06/26] Allow IO in extension hovers --- .../LanguageServer/Handlers/TextDocument/Hover.hs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs b/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs index adccc0e..05a9938 100644 --- a/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs +++ b/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs @@ -6,6 +6,7 @@ module Curry.LanguageServer.Handlers.TextDocument.Hover (hoverHandler) where import Control.Applicative ((<|>)) import Control.Lens ((^.)) +import Control.Monad.Extra (mapMaybeM) import Control.Monad.IO.Class (MonadIO (..)) import Control.Monad.Trans (lift) import Control.Monad.Trans.Maybe (MaybeT (..)) @@ -22,7 +23,7 @@ import Curry.LanguageServer.Utils.Syntax (moduleIdentifier) import Curry.LanguageServer.Utils.Sema (ModuleAST, TypedSpanInfo (..)) import Curry.LanguageServer.Utils.Uri (normalizeUriWithPath) import Curry.LanguageServer.Monad (LSM, getStore) -import Data.Maybe (listToMaybe, maybeToList, mapMaybe) +import Data.Maybe (listToMaybe, maybeToList) import qualified Data.Text as T import qualified Language.LSP.Server as S import qualified Language.LSP.Protocol.Types as J @@ -47,7 +48,7 @@ fetchHover store entry pos = runMaybeT $ do ast <- liftMaybe entry.moduleAST cfg <- lift S.getConfig let baseHover = maybeToList $ qualIdentHover store ast pos <|> typedSpanInfoHover ast pos - extHovers = mapMaybe (\ext -> extensionHover ext ast pos) cfg.extensions + extHovers <- mapMaybeM (\ext -> extensionHover ext ast pos) cfg.extensions hover <- liftMaybe . joinHovers $ baseHover ++ extHovers lift $ infoM $ "Found hover: " <> previewHover hover return hover @@ -70,10 +71,10 @@ typedSpanInfoHover ast@(moduleIdentifier -> mid) pos = do return $ J.Hover contents range -extensionHover :: Extension -> ModuleAST -> J.Position -> Maybe J.Hover +extensionHover :: MonadIO m => Extension -> ModuleAST -> J.Position -> m (Maybe J.Hover) extensionHover e _ _ = case e.extensionPoint of - ExtensionPointHover -> Nothing -- TODO - _ -> Nothing + ExtensionPointHover -> return Nothing -- TODO + _ -> return Nothing previewHover :: J.Hover -> T.Text previewHover = T.unlines . (previewMarkupContent <$>) . normalizeHoverContents . (^. J.contents) From 50e8680253c45d9eff21f6fe95042e13772fc7e8 Mon Sep 17 00:00:00 2001 From: fwcd Date: Wed, 4 Dec 2024 02:06:18 +0100 Subject: [PATCH 07/26] Implement basic hover extensions --- .../Handlers/TextDocument/Hover.hs | 34 ++++++++++++++++--- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs b/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs index 05a9938..c8091a1 100644 --- a/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs +++ b/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs @@ -1,4 +1,4 @@ -{-# LANGUAGE FlexibleContexts, OverloadedStrings, OverloadedRecordDot, TypeOperators, ViewPatterns #-} +{-# LANGUAGE FlexibleContexts, NumericUnderscores, OverloadedStrings, OverloadedRecordDot, TypeOperators, ViewPatterns #-} {-# OPTIONS_GHC -Wno-deprecations #-} module Curry.LanguageServer.Handlers.TextDocument.Hover (hoverHandler) where @@ -30,6 +30,9 @@ import qualified Language.LSP.Protocol.Types as J import qualified Language.LSP.Protocol.Lens as J import qualified Language.LSP.Protocol.Message as J import Language.LSP.Server (MonadLsp) +import System.Exit (ExitCode (..)) +import System.Process (readCreateProcessWithExitCode, shell, CreateProcess (..)) +import System.Timeout (timeout) hoverHandler :: S.Handlers LSM hoverHandler = S.requestHandler J.SMethod_TextDocumentHover $ \req responder -> do @@ -48,7 +51,7 @@ fetchHover store entry pos = runMaybeT $ do ast <- liftMaybe entry.moduleAST cfg <- lift S.getConfig let baseHover = maybeToList $ qualIdentHover store ast pos <|> typedSpanInfoHover ast pos - extHovers <- mapMaybeM (\ext -> extensionHover ext ast pos) cfg.extensions + extHovers <- mapMaybeM (extensionHover ast pos) cfg.extensions hover <- liftMaybe . joinHovers $ baseHover ++ extHovers lift $ infoM $ "Found hover: " <> previewHover hover return hover @@ -71,9 +74,30 @@ typedSpanInfoHover ast@(moduleIdentifier -> mid) pos = do return $ J.Hover contents range -extensionHover :: MonadIO m => Extension -> ModuleAST -> J.Position -> m (Maybe J.Hover) -extensionHover e _ _ = case e.extensionPoint of - ExtensionPointHover -> return Nothing -- TODO +extensionHover :: MonadIO m => ModuleAST -> J.Position -> Extension -> m (Maybe J.Hover) +extensionHover ast pos e = case e.extensionPoint of + ExtensionPointHover -> runMaybeT $ do + TypedSpanInfo _ _ spi <- liftMaybe $ findTypeAtPos ast pos + + let timeoutSecs = 10 + timeoutMicros = timeoutSecs * 1_000_000 + -- TODO: Template parameters + -- TODO: cwd + procOpts = shell (unwords (T.unpack <$> (e.executable : e.args))) + + (exitCode, out, err) <- MaybeT $ liftIO $ timeout timeoutMicros $ readCreateProcessWithExitCode procOpts "" + + let simpleCodeBlock s' + | null s' = "" + | otherwise = "```\n" <> T.pack s' <> "\n```" + text = case exitCode of + ExitSuccess -> simpleCodeBlock out + _ -> "_Extension " <> e.name <> " timed out after " <> T.pack (show timeoutSecs) <> " seconds_" + <> simpleCodeBlock err + contents = J.InL $ J.MarkupContent J.MarkupKind_Markdown text + range = currySpanInfo2Range spi + + return $ J.Hover contents range _ -> return Nothing previewHover :: J.Hover -> T.Text From cb10bf114a38635d16b852d84e56c81dcb5264ca Mon Sep 17 00:00:00 2001 From: fwcd Date: Wed, 4 Dec 2024 02:10:54 +0100 Subject: [PATCH 08/26] Update extension hover formatting --- .../LanguageServer/Handlers/TextDocument/Hover.hs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs b/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs index c8091a1..6a86717 100644 --- a/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs +++ b/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs @@ -87,13 +87,13 @@ extensionHover ast pos e = case e.extensionPoint of (exitCode, out, err) <- MaybeT $ liftIO $ timeout timeoutMicros $ readCreateProcessWithExitCode procOpts "" - let simpleCodeBlock s' - | null s' = "" - | otherwise = "```\n" <> T.pack s' <> "\n```" + let simpleCodeBlock t + | T.null t = "" + | otherwise = "```\n" <> t <> "\n```" text = case exitCode of - ExitSuccess -> simpleCodeBlock out + ExitSuccess -> T.pack out _ -> "_Extension " <> e.name <> " timed out after " <> T.pack (show timeoutSecs) <> " seconds_" - <> simpleCodeBlock err + <> simpleCodeBlock (T.pack err) contents = J.InL $ J.MarkupContent J.MarkupKind_Markdown text range = currySpanInfo2Range spi @@ -122,7 +122,7 @@ joinMarkupContent cs = foldr1 mergeMarkupContent cs mergeMarkupContent :: J.MarkupContent -> J.MarkupContent -> J.MarkupContent mergeMarkupContent (normalizeToMarkdown -> J.MarkupContent _ t1) (normalizeToMarkdown -> J.MarkupContent _ t2) = - J.MarkupContent J.MarkupKind_Markdown $ T.unlines [t1, t2] + J.MarkupContent J.MarkupKind_Markdown $ T.unlines [t1, "", "---", "", t2] emptyMarkupContent :: J.MarkupContent emptyMarkupContent = J.MarkupContent J.MarkupKind_PlainText "" From 0445022143605d09698e9b092d80557d32d17058 Mon Sep 17 00:00:00 2001 From: fwcd Date: Wed, 4 Dec 2024 02:18:41 +0100 Subject: [PATCH 09/26] Improve exit message --- src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs b/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs index 6a86717..dd075d7 100644 --- a/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs +++ b/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs @@ -92,8 +92,10 @@ extensionHover ast pos e = case e.extensionPoint of | otherwise = "```\n" <> t <> "\n```" text = case exitCode of ExitSuccess -> T.pack out - _ -> "_Extension " <> e.name <> " timed out after " <> T.pack (show timeoutSecs) <> " seconds_" - <> simpleCodeBlock (T.pack err) + _ -> T.unlines + [ "_Extension `" <> e.name <> "` exited with " <> T.pack (show exitCode) <> "_" + , simpleCodeBlock (T.pack err) + ] contents = J.InL $ J.MarkupContent J.MarkupKind_Markdown text range = currySpanInfo2Range spi From e3a87388a2edfccd9e46802ef6e081887d404610 Mon Sep 17 00:00:00 2001 From: fwcd Date: Wed, 4 Dec 2024 02:20:40 +0100 Subject: [PATCH 10/26] Run hover extensions without shell --- src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs b/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs index dd075d7..50ff67a 100644 --- a/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs +++ b/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs @@ -31,7 +31,7 @@ import qualified Language.LSP.Protocol.Lens as J import qualified Language.LSP.Protocol.Message as J import Language.LSP.Server (MonadLsp) import System.Exit (ExitCode (..)) -import System.Process (readCreateProcessWithExitCode, shell, CreateProcess (..)) +import System.Process (readCreateProcessWithExitCode, proc) import System.Timeout (timeout) hoverHandler :: S.Handlers LSM @@ -83,7 +83,7 @@ extensionHover ast pos e = case e.extensionPoint of timeoutMicros = timeoutSecs * 1_000_000 -- TODO: Template parameters -- TODO: cwd - procOpts = shell (unwords (T.unpack <$> (e.executable : e.args))) + procOpts = proc (T.unpack e.executable) (T.unpack <$> e.args) (exitCode, out, err) <- MaybeT $ liftIO $ timeout timeoutMicros $ readCreateProcessWithExitCode procOpts "" From 3491d044006b8619e99a8350817d421adaa86f76 Mon Sep 17 00:00:00 2001 From: fwcd Date: Wed, 4 Dec 2024 02:33:01 +0100 Subject: [PATCH 11/26] Manually define Extension ToJSON/FromJSON This lets us define default values --- src/Curry/LanguageServer/Extension.hs | 28 +++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/src/Curry/LanguageServer/Extension.hs b/src/Curry/LanguageServer/Extension.hs index 80344f0..72db953 100644 --- a/src/Curry/LanguageServer/Extension.hs +++ b/src/Curry/LanguageServer/Extension.hs @@ -1,11 +1,11 @@ -{-# LANGUAGE DeriveGeneric, OverloadedStrings, TypeApplications #-} +{-# LANGUAGE OverloadedRecordDot, OverloadedStrings, RecordWildCards, TypeApplications #-} module Curry.LanguageServer.Extension ( ExtensionPoint (..), Extension (..) ) where -import Data.Aeson (FromJSON (..), ToJSON (..)) +import Data.Aeson (FromJSON (..), ToJSON (..), (.:?), (.!=), (.=), object, withObject) +import Data.Default (Default (..)) import qualified Data.Text as T -import GHC.Generics (Generic (..)) data ExtensionPoint = ExtensionPointHover | ExtensionPointUnknown T.Text @@ -17,11 +17,31 @@ data Extension = Extension , executable :: T.Text , args :: [T.Text] } - deriving (Show, Eq, Generic) + deriving (Show, Eq) + +instance Default Extension where + def = Extension + { name = "Anonymous Extension" + , extensionPoint = ExtensionPointHover + , executable = "echo" + , args = [] + } instance FromJSON Extension where + parseJSON = withObject "Extension" $ \e -> do + name <- e .:? "name" .!= (def @Extension).name + extensionPoint <- e .:? "extensionPoint" .!= (def @Extension).extensionPoint + executable <- e .:? "executable" .!= (def @Extension).executable + args <- e .:? "args" .!= (def @Extension).args + return Extension {..} instance ToJSON Extension where + toJSON Extension {..} = object + [ "name" .= name + , "extensionPoint" .= extensionPoint + , "executable" .= executable + , "args" .= args + ] instance FromJSON ExtensionPoint where parseJSON v = do From 245a61b7a669a68b5a46e4d6455c093e666cec5a Mon Sep 17 00:00:00 2001 From: fwcd Date: Wed, 4 Dec 2024 02:39:26 +0100 Subject: [PATCH 12/26] Add support for extension output formats --- src/Curry/LanguageServer/Extension.hs | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/Curry/LanguageServer/Extension.hs b/src/Curry/LanguageServer/Extension.hs index 72db953..bb7cb69 100644 --- a/src/Curry/LanguageServer/Extension.hs +++ b/src/Curry/LanguageServer/Extension.hs @@ -1,6 +1,6 @@ {-# LANGUAGE OverloadedRecordDot, OverloadedStrings, RecordWildCards, TypeApplications #-} module Curry.LanguageServer.Extension - ( ExtensionPoint (..), Extension (..) + ( ExtensionPoint (..), ExtensionOutputFormat (..), Extension (..) ) where import Data.Aeson (FromJSON (..), ToJSON (..), (.:?), (.!=), (.=), object, withObject) @@ -11,9 +11,15 @@ data ExtensionPoint = ExtensionPointHover | ExtensionPointUnknown T.Text deriving (Show, Eq) +data ExtensionOutputFormat = ExtensionOutputFormatPlaintext + | ExtensionOutputFormatMarkdown + | ExtensionOutputFormatUnknown T.Text + deriving (Show, Eq) + data Extension = Extension { name :: T.Text , extensionPoint :: ExtensionPoint + , outputFormat :: ExtensionOutputFormat , executable :: T.Text , args :: [T.Text] } @@ -23,6 +29,7 @@ instance Default Extension where def = Extension { name = "Anonymous Extension" , extensionPoint = ExtensionPointHover + , outputFormat = ExtensionOutputFormatPlaintext , executable = "echo" , args = [] } @@ -31,6 +38,7 @@ instance FromJSON Extension where parseJSON = withObject "Extension" $ \e -> do name <- e .:? "name" .!= (def @Extension).name extensionPoint <- e .:? "extensionPoint" .!= (def @Extension).extensionPoint + outputFormat <- e .:? "outputFormat" .!= (def @Extension).outputFormat executable <- e .:? "executable" .!= (def @Extension).executable args <- e .:? "args" .!= (def @Extension).args return Extension {..} @@ -39,6 +47,7 @@ instance ToJSON Extension where toJSON Extension {..} = object [ "name" .= name , "extensionPoint" .= extensionPoint + , "outputFormat" .= outputFormat , "executable" .= executable , "args" .= args ] @@ -54,3 +63,17 @@ instance ToJSON ExtensionPoint where toJSON p = toJSON @T.Text $ case p of ExtensionPointHover -> "hover" ExtensionPointUnknown s -> s + +instance FromJSON ExtensionOutputFormat where + parseJSON v = do + s <- parseJSON v + return $ case s :: T.Text of + "plaintext" -> ExtensionOutputFormatPlaintext + "markdown" -> ExtensionOutputFormatMarkdown + _ -> ExtensionOutputFormatUnknown s + +instance ToJSON ExtensionOutputFormat where + toJSON p = toJSON @T.Text $ case p of + ExtensionOutputFormatPlaintext -> "plaintext" + ExtensionOutputFormatMarkdown -> "markdown" + ExtensionOutputFormatUnknown s -> s From 846a1acfbc03000b76d0249a9c6bf96b3bd08689 Mon Sep 17 00:00:00 2001 From: fwcd Date: Wed, 4 Dec 2024 02:39:51 +0100 Subject: [PATCH 13/26] Format extension data type definition --- src/Curry/LanguageServer/Extension.hs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Curry/LanguageServer/Extension.hs b/src/Curry/LanguageServer/Extension.hs index bb7cb69..ba72d3f 100644 --- a/src/Curry/LanguageServer/Extension.hs +++ b/src/Curry/LanguageServer/Extension.hs @@ -17,11 +17,11 @@ data ExtensionOutputFormat = ExtensionOutputFormatPlaintext deriving (Show, Eq) data Extension = Extension - { name :: T.Text + { name :: T.Text , extensionPoint :: ExtensionPoint - , outputFormat :: ExtensionOutputFormat - , executable :: T.Text - , args :: [T.Text] + , outputFormat :: ExtensionOutputFormat + , executable :: T.Text + , args :: [T.Text] } deriving (Show, Eq) From b6b0ba21126596c7529a529e18cde755b2fddf61 Mon Sep 17 00:00:00 2001 From: fwcd Date: Wed, 4 Dec 2024 02:42:21 +0100 Subject: [PATCH 14/26] Format hover extensions depending on output format --- src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs b/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs index 50ff67a..087b76a 100644 --- a/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs +++ b/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs @@ -13,7 +13,7 @@ import Control.Monad.Trans.Maybe (MaybeT (..)) import qualified Curry.LanguageServer.Config as CFG import qualified Curry.LanguageServer.Index.Store as I import qualified Curry.LanguageServer.Index.Symbol as I -import Curry.LanguageServer.Extension (ExtensionPoint (..), Extension (..)) +import Curry.LanguageServer.Extension (ExtensionPoint (..), ExtensionOutputFormat (..), Extension (..)) import Curry.LanguageServer.Utils.Convert (ppPredTypeToText, currySpanInfo2Range) import Curry.LanguageServer.Index.Resolve (resolveAtPos) import Curry.LanguageServer.Utils.General (liftMaybe) @@ -91,7 +91,10 @@ extensionHover ast pos e = case e.extensionPoint of | T.null t = "" | otherwise = "```\n" <> t <> "\n```" text = case exitCode of - ExitSuccess -> T.pack out + ExitSuccess -> + case e.outputFormat of + ExtensionOutputFormatMarkdown -> T.pack out + _ -> simpleCodeBlock (T.pack out) _ -> T.unlines [ "_Extension `" <> e.name <> "` exited with " <> T.pack (show exitCode) <> "_" , simpleCodeBlock (T.pack err) From 55000910ab1042fd7e083bd20b891ef945eb1115 Mon Sep 17 00:00:00 2001 From: fwcd Date: Wed, 4 Dec 2024 02:47:09 +0100 Subject: [PATCH 15/26] Add extension title to hover --- .../LanguageServer/Handlers/TextDocument/Hover.hs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs b/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs index 087b76a..b454600 100644 --- a/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs +++ b/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs @@ -90,13 +90,15 @@ extensionHover ast pos e = case e.extensionPoint of let simpleCodeBlock t | T.null t = "" | otherwise = "```\n" <> t <> "\n```" - text = case exitCode of + text = T.unlines $ case exitCode of ExitSuccess -> - case e.outputFormat of + [ "**" <> e.name <> "**" + , case e.outputFormat of ExtensionOutputFormatMarkdown -> T.pack out _ -> simpleCodeBlock (T.pack out) - _ -> T.unlines - [ "_Extension `" <> e.name <> "` exited with " <> T.pack (show exitCode) <> "_" + ] + _ -> + [ "_Extension **" <> e.name <> "** exited with " <> T.pack (show exitCode) <> "_" , simpleCodeBlock (T.pack err) ] contents = J.InL $ J.MarkupContent J.MarkupKind_Markdown text From 5b701e683e723e33e83645bcfaef395ece3c0f0d Mon Sep 17 00:00:00 2001 From: fwcd Date: Wed, 4 Dec 2024 02:48:23 +0100 Subject: [PATCH 16/26] Add missing line breaks --- src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs b/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs index b454600..d873959 100644 --- a/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs +++ b/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs @@ -93,12 +93,14 @@ extensionHover ast pos e = case e.extensionPoint of text = T.unlines $ case exitCode of ExitSuccess -> [ "**" <> e.name <> "**" + , "" , case e.outputFormat of ExtensionOutputFormatMarkdown -> T.pack out _ -> simpleCodeBlock (T.pack out) ] _ -> [ "_Extension **" <> e.name <> "** exited with " <> T.pack (show exitCode) <> "_" + , "" , simpleCodeBlock (T.pack err) ] contents = J.InL $ J.MarkupContent J.MarkupKind_Markdown text From cd8e9df3cc50f48b04046d38329a7b3aa5c85b6c Mon Sep 17 00:00:00 2001 From: fwcd Date: Wed, 4 Dec 2024 03:03:24 +0100 Subject: [PATCH 17/26] Implement template substitutions --- .../Handlers/TextDocument/Hover.hs | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs b/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs index d873959..3b4ef7b 100644 --- a/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs +++ b/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs @@ -21,9 +21,9 @@ import Curry.LanguageServer.Utils.Logging (debugM, infoM) import Curry.LanguageServer.Utils.Lookup (findTypeAtPos) import Curry.LanguageServer.Utils.Syntax (moduleIdentifier) import Curry.LanguageServer.Utils.Sema (ModuleAST, TypedSpanInfo (..)) -import Curry.LanguageServer.Utils.Uri (normalizeUriWithPath) +import Curry.LanguageServer.Utils.Uri (normalizeUriWithPath, uriToFilePath) import Curry.LanguageServer.Monad (LSM, getStore) -import Data.Maybe (listToMaybe, maybeToList) +import Data.Maybe (listToMaybe, maybeToList, fromMaybe) import qualified Data.Text as T import qualified Language.LSP.Server as S import qualified Language.LSP.Protocol.Types as J @@ -43,15 +43,15 @@ hoverHandler = S.requestHandler J.SMethod_TextDocumentHover $ \req responder -> store <- getStore hover <- runMaybeT $ do entry <- I.getModule normUri - MaybeT $ fetchHover store entry pos + MaybeT $ fetchHover store entry pos uri responder $ Right $ maybe (J.InR J.Null) J.InL hover -fetchHover :: (MonadIO m, MonadLsp CFG.Config m) => I.IndexStore -> I.ModuleStoreEntry -> J.Position -> m (Maybe J.Hover) -fetchHover store entry pos = runMaybeT $ do +fetchHover :: (MonadIO m, MonadLsp CFG.Config m) => I.IndexStore -> I.ModuleStoreEntry -> J.Position -> J.Uri -> m (Maybe J.Hover) +fetchHover store entry pos uri = runMaybeT $ do ast <- liftMaybe entry.moduleAST cfg <- lift S.getConfig let baseHover = maybeToList $ qualIdentHover store ast pos <|> typedSpanInfoHover ast pos - extHovers <- mapMaybeM (extensionHover ast pos) cfg.extensions + extHovers <- mapMaybeM (extensionHover ast pos uri) cfg.extensions hover <- liftMaybe . joinHovers $ baseHover ++ extHovers lift $ infoM $ "Found hover: " <> previewHover hover return hover @@ -74,16 +74,20 @@ typedSpanInfoHover ast@(moduleIdentifier -> mid) pos = do return $ J.Hover contents range -extensionHover :: MonadIO m => ModuleAST -> J.Position -> Extension -> m (Maybe J.Hover) -extensionHover ast pos e = case e.extensionPoint of +extensionHover :: MonadIO m => ModuleAST -> J.Position -> J.Uri -> Extension -> m (Maybe J.Hover) +extensionHover ast pos@(J.Position l c) uri e = case e.extensionPoint of ExtensionPointHover -> runMaybeT $ do TypedSpanInfo _ _ spi <- liftMaybe $ findTypeAtPos ast pos - let timeoutSecs = 10 - timeoutMicros = timeoutSecs * 1_000_000 - -- TODO: Template parameters - -- TODO: cwd - procOpts = proc (T.unpack e.executable) (T.unpack <$> e.args) + let timeoutSecs = 10 + timeoutMicros = timeoutSecs * 1_000_000 + templateParams = [ ("sourceFile", T.pack (fromMaybe "" (uriToFilePath uri))) + , ("sourceUri", T.pack (show uri)) + , ("line", T.pack (show l)) + , ("column", T.pack (show c)) + ] :: [(T.Text, T.Text)] + evalTemplate t = foldr (\(p, r) -> T.replace ("{" <> p <> "}") r) t templateParams + procOpts = proc (T.unpack e.executable) (T.unpack . (evalTemplate :: T.Text -> T.Text) <$> e.args) (exitCode, out, err) <- MaybeT $ liftIO $ timeout timeoutMicros $ readCreateProcessWithExitCode procOpts "" From c71ecb429a5a9cfa35d69743de6e54c1894506b1 Mon Sep 17 00:00:00 2001 From: fwcd Date: Wed, 4 Dec 2024 03:05:18 +0100 Subject: [PATCH 18/26] Factor out applyParam --- src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs b/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs index 3b4ef7b..d195896 100644 --- a/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs +++ b/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs @@ -86,7 +86,8 @@ extensionHover ast pos@(J.Position l c) uri e = case e.extensionPoint of , ("line", T.pack (show l)) , ("column", T.pack (show c)) ] :: [(T.Text, T.Text)] - evalTemplate t = foldr (\(p, r) -> T.replace ("{" <> p <> "}") r) t templateParams + applyParam p v = T.replace p ("{" <> v <> "}") + evalTemplate t = foldr (uncurry applyParam) t templateParams procOpts = proc (T.unpack e.executable) (T.unpack . (evalTemplate :: T.Text -> T.Text) <$> e.args) (exitCode, out, err) <- MaybeT $ liftIO $ timeout timeoutMicros $ readCreateProcessWithExitCode procOpts "" From 54644604913c556a10efeae04443496a24c12ae0 Mon Sep 17 00:00:00 2001 From: fwcd Date: Wed, 4 Dec 2024 03:10:25 +0100 Subject: [PATCH 19/26] Add some more template parameters --- src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs b/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs index d195896..9c8961d 100644 --- a/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs +++ b/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs @@ -14,7 +14,7 @@ import qualified Curry.LanguageServer.Config as CFG import qualified Curry.LanguageServer.Index.Store as I import qualified Curry.LanguageServer.Index.Symbol as I import Curry.LanguageServer.Extension (ExtensionPoint (..), ExtensionOutputFormat (..), Extension (..)) -import Curry.LanguageServer.Utils.Convert (ppPredTypeToText, currySpanInfo2Range) +import Curry.LanguageServer.Utils.Convert (ppPredTypeToText, currySpanInfo2Range, ppToText) import Curry.LanguageServer.Index.Resolve (resolveAtPos) import Curry.LanguageServer.Utils.General (liftMaybe) import Curry.LanguageServer.Utils.Logging (debugM, infoM) @@ -75,9 +75,9 @@ typedSpanInfoHover ast@(moduleIdentifier -> mid) pos = do return $ J.Hover contents range extensionHover :: MonadIO m => ModuleAST -> J.Position -> J.Uri -> Extension -> m (Maybe J.Hover) -extensionHover ast pos@(J.Position l c) uri e = case e.extensionPoint of +extensionHover ast@(moduleIdentifier -> mid) pos@(J.Position l c) uri e = case e.extensionPoint of ExtensionPointHover -> runMaybeT $ do - TypedSpanInfo _ _ spi <- liftMaybe $ findTypeAtPos ast pos + TypedSpanInfo expr ty spi <- liftMaybe $ findTypeAtPos ast pos let timeoutSecs = 10 timeoutMicros = timeoutSecs * 1_000_000 @@ -85,6 +85,9 @@ extensionHover ast pos@(J.Position l c) uri e = case e.extensionPoint of , ("sourceUri", T.pack (show uri)) , ("line", T.pack (show l)) , ("column", T.pack (show c)) + , ("module", ppToText mid) + , ("expression", expr) + , ("type", maybe "?" (ppPredTypeToText mid) ty) ] :: [(T.Text, T.Text)] applyParam p v = T.replace p ("{" <> v <> "}") evalTemplate t = foldr (uncurry applyParam) t templateParams From db0aa41d8748e6cc63080be0da1457d3f8dfacb6 Mon Sep 17 00:00:00 2001 From: fwcd Date: Wed, 4 Dec 2024 03:15:00 +0100 Subject: [PATCH 20/26] Fix typo --- src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs b/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs index 9c8961d..d6c3dbf 100644 --- a/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs +++ b/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs @@ -89,7 +89,7 @@ extensionHover ast@(moduleIdentifier -> mid) pos@(J.Position l c) uri e = case e , ("expression", expr) , ("type", maybe "?" (ppPredTypeToText mid) ty) ] :: [(T.Text, T.Text)] - applyParam p v = T.replace p ("{" <> v <> "}") + applyParam p = T.replace ("{" <> p <> "}") evalTemplate t = foldr (uncurry applyParam) t templateParams procOpts = proc (T.unpack e.executable) (T.unpack . (evalTemplate :: T.Text -> T.Text) <$> e.args) From ed04ed22db373b94cd400c04de245e67180a01fe Mon Sep 17 00:00:00 2001 From: fwcd Date: Wed, 4 Dec 2024 03:21:36 +0100 Subject: [PATCH 21/26] Update naming convention for template parameters --- src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs b/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs index d6c3dbf..d815e93 100644 --- a/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs +++ b/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs @@ -81,11 +81,11 @@ extensionHover ast@(moduleIdentifier -> mid) pos@(J.Position l c) uri e = case e let timeoutSecs = 10 timeoutMicros = timeoutSecs * 1_000_000 - templateParams = [ ("sourceFile", T.pack (fromMaybe "" (uriToFilePath uri))) - , ("sourceUri", T.pack (show uri)) + templateParams = [ ("currentFile", T.pack (fromMaybe "" (uriToFilePath uri))) + , ("currentUri", T.pack (show uri)) + , ("currentModule", ppToText mid) , ("line", T.pack (show l)) , ("column", T.pack (show c)) - , ("module", ppToText mid) , ("expression", expr) , ("type", maybe "?" (ppPredTypeToText mid) ty) ] :: [(T.Text, T.Text)] From 4514ced53221c00066dfe5658cedc319703c1f66 Mon Sep 17 00:00:00 2001 From: fwcd Date: Wed, 4 Dec 2024 03:22:15 +0100 Subject: [PATCH 22/26] Fix URI template parameter --- src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs b/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs index d815e93..ff15b53 100644 --- a/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs +++ b/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs @@ -82,7 +82,7 @@ extensionHover ast@(moduleIdentifier -> mid) pos@(J.Position l c) uri e = case e let timeoutSecs = 10 timeoutMicros = timeoutSecs * 1_000_000 templateParams = [ ("currentFile", T.pack (fromMaybe "" (uriToFilePath uri))) - , ("currentUri", T.pack (show uri)) + , ("currentUri", J.getUri uri) , ("currentModule", ppToText mid) , ("line", T.pack (show l)) , ("column", T.pack (show c)) From 7114e9e961c95d8406bf0975b65198d66359eeea Mon Sep 17 00:00:00 2001 From: fwcd Date: Wed, 4 Dec 2024 03:29:26 +0100 Subject: [PATCH 23/26] Always provide extension hover --- .../LanguageServer/Handlers/TextDocument/Hover.hs | 12 +++++------- src/Curry/LanguageServer/Utils/Sema.hs | 6 +++++- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs b/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs index ff15b53..a07e6e2 100644 --- a/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs +++ b/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs @@ -77,17 +77,16 @@ typedSpanInfoHover ast@(moduleIdentifier -> mid) pos = do extensionHover :: MonadIO m => ModuleAST -> J.Position -> J.Uri -> Extension -> m (Maybe J.Hover) extensionHover ast@(moduleIdentifier -> mid) pos@(J.Position l c) uri e = case e.extensionPoint of ExtensionPointHover -> runMaybeT $ do - TypedSpanInfo expr ty spi <- liftMaybe $ findTypeAtPos ast pos - - let timeoutSecs = 10 + let tspi = findTypeAtPos ast pos + timeoutSecs = 10 timeoutMicros = timeoutSecs * 1_000_000 templateParams = [ ("currentFile", T.pack (fromMaybe "" (uriToFilePath uri))) , ("currentUri", J.getUri uri) , ("currentModule", ppToText mid) , ("line", T.pack (show l)) , ("column", T.pack (show c)) - , ("expression", expr) - , ("type", maybe "?" (ppPredTypeToText mid) ty) + , ("expression", maybe "" (.exprText) tspi) + , ("type", maybe "" (ppPredTypeToText mid) ((.typeAnnotation) =<< tspi)) ] :: [(T.Text, T.Text)] applyParam p = T.replace ("{" <> p <> "}") evalTemplate t = foldr (uncurry applyParam) t templateParams @@ -112,9 +111,8 @@ extensionHover ast@(moduleIdentifier -> mid) pos@(J.Position l c) uri e = case e , simpleCodeBlock (T.pack err) ] contents = J.InL $ J.MarkupContent J.MarkupKind_Markdown text - range = currySpanInfo2Range spi - return $ J.Hover contents range + return $ J.Hover contents Nothing _ -> return Nothing previewHover :: J.Hover -> T.Text diff --git a/src/Curry/LanguageServer/Utils/Sema.hs b/src/Curry/LanguageServer/Utils/Sema.hs index 34f319d..04fa9f3 100644 --- a/src/Curry/LanguageServer/Utils/Sema.hs +++ b/src/Curry/LanguageServer/Utils/Sema.hs @@ -27,7 +27,11 @@ untypedTopLevelDecls (CS.Module _ _ _ _ _ _ decls) = untypedDecls where typeSigIdents = S.fromList [i | CS.TypeSig _ is _ <- decls, i <- is] untypedDecls = [(spi, i, t) | CS.FunctionDecl spi t i _ <- decls, i `S.notMember` typeSigIdents] -data TypedSpanInfo a = TypedSpanInfo T.Text a CSPI.SpanInfo +data TypedSpanInfo a = TypedSpanInfo + { exprText :: T.Text + , typeAnnotation :: a + , spanInfo :: CSPI.SpanInfo + } deriving (Show, Eq) class HasTypedSpanInfos e a where From 2c922d0c20cdd846398d753b99701bdfc801d1bd Mon Sep 17 00:00:00 2001 From: fwcd Date: Wed, 4 Dec 2024 03:35:27 +0100 Subject: [PATCH 24/26] Support the `showOutputOnError` key in extensions --- src/Curry/LanguageServer/Extension.hs | 44 ++++++++++--------- .../Handlers/TextDocument/Hover.hs | 31 +++++++------ 2 files changed, 41 insertions(+), 34 deletions(-) diff --git a/src/Curry/LanguageServer/Extension.hs b/src/Curry/LanguageServer/Extension.hs index ba72d3f..2fd86ab 100644 --- a/src/Curry/LanguageServer/Extension.hs +++ b/src/Curry/LanguageServer/Extension.hs @@ -17,39 +17,43 @@ data ExtensionOutputFormat = ExtensionOutputFormatPlaintext deriving (Show, Eq) data Extension = Extension - { name :: T.Text - , extensionPoint :: ExtensionPoint - , outputFormat :: ExtensionOutputFormat - , executable :: T.Text - , args :: [T.Text] + { name :: T.Text + , extensionPoint :: ExtensionPoint + , outputFormat :: ExtensionOutputFormat + , showOutputOnError :: Bool + , executable :: T.Text + , args :: [T.Text] } deriving (Show, Eq) instance Default Extension where def = Extension - { name = "Anonymous Extension" - , extensionPoint = ExtensionPointHover - , outputFormat = ExtensionOutputFormatPlaintext - , executable = "echo" - , args = [] + { name = "Anonymous Extension" + , extensionPoint = ExtensionPointHover + , outputFormat = ExtensionOutputFormatPlaintext + , showOutputOnError = False + , executable = "echo" + , args = [] } instance FromJSON Extension where parseJSON = withObject "Extension" $ \e -> do - name <- e .:? "name" .!= (def @Extension).name - extensionPoint <- e .:? "extensionPoint" .!= (def @Extension).extensionPoint - outputFormat <- e .:? "outputFormat" .!= (def @Extension).outputFormat - executable <- e .:? "executable" .!= (def @Extension).executable - args <- e .:? "args" .!= (def @Extension).args + name <- e .:? "name" .!= (def @Extension).name + extensionPoint <- e .:? "extensionPoint" .!= (def @Extension).extensionPoint + outputFormat <- e .:? "outputFormat" .!= (def @Extension).outputFormat + showOutputOnError <- e .:? "showOutputOnError" .!= (def @Extension).showOutputOnError + executable <- e .:? "executable" .!= (def @Extension).executable + args <- e .:? "args" .!= (def @Extension).args return Extension {..} instance ToJSON Extension where toJSON Extension {..} = object - [ "name" .= name - , "extensionPoint" .= extensionPoint - , "outputFormat" .= outputFormat - , "executable" .= executable - , "args" .= args + [ "name" .= name + , "extensionPoint" .= extensionPoint + , "outputFormat" .= outputFormat + , "showOutputOnError" .= showOutputOnError + , "executable" .= executable + , "args" .= args ] instance FromJSON ExtensionPoint where diff --git a/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs b/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs index a07e6e2..cccd864 100644 --- a/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs +++ b/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs @@ -97,20 +97,23 @@ extensionHover ast@(moduleIdentifier -> mid) pos@(J.Position l c) uri e = case e let simpleCodeBlock t | T.null t = "" | otherwise = "```\n" <> t <> "\n```" - text = T.unlines $ case exitCode of - ExitSuccess -> - [ "**" <> e.name <> "**" - , "" - , case e.outputFormat of - ExtensionOutputFormatMarkdown -> T.pack out - _ -> simpleCodeBlock (T.pack out) - ] - _ -> - [ "_Extension **" <> e.name <> "** exited with " <> T.pack (show exitCode) <> "_" - , "" - , simpleCodeBlock (T.pack err) - ] - contents = J.InL $ J.MarkupContent J.MarkupKind_Markdown text + + text <- case exitCode of + ExitSuccess -> return $ T.unlines + [ "**" <> e.name <> "**" + , "" + , case e.outputFormat of + ExtensionOutputFormatMarkdown -> T.pack out + _ -> simpleCodeBlock (T.pack out) + ] + _ | e.showOutputOnError -> return $ T.unlines + [ "_Extension **" <> e.name <> "** exited with " <> T.pack (show exitCode) <> "_" + , "" + , simpleCodeBlock (T.pack err) + ] + | otherwise -> liftMaybe Nothing + + let contents = J.InL $ J.MarkupContent J.MarkupKind_Markdown text return $ J.Hover contents Nothing _ -> return Nothing From 55a15a35a1645c18f69fc06b460206e5e4289da5 Mon Sep 17 00:00:00 2001 From: fwcd Date: Fri, 6 Dec 2024 03:03:26 +0100 Subject: [PATCH 25/26] Expose more information to hover extensions --- .../LanguageServer/Handlers/TextDocument/Hover.hs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs b/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs index cccd864..500d2db 100644 --- a/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs +++ b/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs @@ -3,6 +3,7 @@ module Curry.LanguageServer.Handlers.TextDocument.Hover (hoverHandler) where -- Curry Compiler Libraries + Dependencies +import qualified Curry.Base.Ident as CI import Control.Applicative ((<|>)) import Control.Lens ((^.)) @@ -18,7 +19,7 @@ import Curry.LanguageServer.Utils.Convert (ppPredTypeToText, currySpanInfo2Range import Curry.LanguageServer.Index.Resolve (resolveAtPos) import Curry.LanguageServer.Utils.General (liftMaybe) import Curry.LanguageServer.Utils.Logging (debugM, infoM) -import Curry.LanguageServer.Utils.Lookup (findTypeAtPos) +import Curry.LanguageServer.Utils.Lookup (findTypeAtPos, findQualIdentAtPos, findModuleIdentAtPos) import Curry.LanguageServer.Utils.Syntax (moduleIdentifier) import Curry.LanguageServer.Utils.Sema (ModuleAST, TypedSpanInfo (..)) import Curry.LanguageServer.Utils.Uri (normalizeUriWithPath, uriToFilePath) @@ -77,7 +78,9 @@ typedSpanInfoHover ast@(moduleIdentifier -> mid) pos = do extensionHover :: MonadIO m => ModuleAST -> J.Position -> J.Uri -> Extension -> m (Maybe J.Hover) extensionHover ast@(moduleIdentifier -> mid) pos@(J.Position l c) uri e = case e.extensionPoint of ExtensionPointHover -> runMaybeT $ do - let tspi = findTypeAtPos ast pos + let hoveredTy = findTypeAtPos ast pos + hoveredQid = fst <$> findQualIdentAtPos ast pos + hoveredMid = fst <$> findModuleIdentAtPos ast pos timeoutSecs = 10 timeoutMicros = timeoutSecs * 1_000_000 templateParams = [ ("currentFile", T.pack (fromMaybe "" (uriToFilePath uri))) @@ -85,8 +88,10 @@ extensionHover ast@(moduleIdentifier -> mid) pos@(J.Position l c) uri e = case e , ("currentModule", ppToText mid) , ("line", T.pack (show l)) , ("column", T.pack (show c)) - , ("expression", maybe "" (.exprText) tspi) - , ("type", maybe "" (ppPredTypeToText mid) ((.typeAnnotation) =<< tspi)) + , ("expression", maybe "" (.exprText) hoveredTy) + , ("type", maybe "" (ppPredTypeToText mid) ((.typeAnnotation) =<< hoveredTy)) + , ("identifier", maybe "" (ppToText . CI.qidIdent) hoveredQid) + , ("module", maybe "" ppToText (hoveredMid <|> (CI.qidModule =<< hoveredQid))) ] :: [(T.Text, T.Text)] applyParam p = T.replace ("{" <> p <> "}") evalTemplate t = foldr (uncurry applyParam) t templateParams From daa94903157441422a25a092bd8a35df2aea5a00 Mon Sep 17 00:00:00 2001 From: fwcd Date: Fri, 6 Dec 2024 03:10:47 +0100 Subject: [PATCH 26/26] Use resolveAtPos for hover extensions --- .../Handlers/TextDocument/Hover.hs | 23 ++++++++----------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs b/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs index 500d2db..bb64e86 100644 --- a/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs +++ b/src/Curry/LanguageServer/Handlers/TextDocument/Hover.hs @@ -2,9 +2,6 @@ {-# OPTIONS_GHC -Wno-deprecations #-} module Curry.LanguageServer.Handlers.TextDocument.Hover (hoverHandler) where --- Curry Compiler Libraries + Dependencies -import qualified Curry.Base.Ident as CI - import Control.Applicative ((<|>)) import Control.Lens ((^.)) import Control.Monad.Extra (mapMaybeM) @@ -19,7 +16,8 @@ import Curry.LanguageServer.Utils.Convert (ppPredTypeToText, currySpanInfo2Range import Curry.LanguageServer.Index.Resolve (resolveAtPos) import Curry.LanguageServer.Utils.General (liftMaybe) import Curry.LanguageServer.Utils.Logging (debugM, infoM) -import Curry.LanguageServer.Utils.Lookup (findTypeAtPos, findQualIdentAtPos, findModuleIdentAtPos) +import Curry.LanguageServer.Utils.Lookup (findTypeAtPos) +import Curry.LanguageServer.Index.Symbol (symbolParentIdent) import Curry.LanguageServer.Utils.Syntax (moduleIdentifier) import Curry.LanguageServer.Utils.Sema (ModuleAST, TypedSpanInfo (..)) import Curry.LanguageServer.Utils.Uri (normalizeUriWithPath, uriToFilePath) @@ -52,7 +50,7 @@ fetchHover store entry pos uri = runMaybeT $ do ast <- liftMaybe entry.moduleAST cfg <- lift S.getConfig let baseHover = maybeToList $ qualIdentHover store ast pos <|> typedSpanInfoHover ast pos - extHovers <- mapMaybeM (extensionHover ast pos uri) cfg.extensions + extHovers <- mapMaybeM (extensionHover store ast pos uri) cfg.extensions hover <- liftMaybe . joinHovers $ baseHover ++ extHovers lift $ infoM $ "Found hover: " <> previewHover hover return hover @@ -75,12 +73,10 @@ typedSpanInfoHover ast@(moduleIdentifier -> mid) pos = do return $ J.Hover contents range -extensionHover :: MonadIO m => ModuleAST -> J.Position -> J.Uri -> Extension -> m (Maybe J.Hover) -extensionHover ast@(moduleIdentifier -> mid) pos@(J.Position l c) uri e = case e.extensionPoint of +extensionHover :: MonadIO m => I.IndexStore -> ModuleAST -> J.Position -> J.Uri -> Extension -> m (Maybe J.Hover) +extensionHover store ast@(moduleIdentifier -> mid) pos@(J.Position l c) uri e = case e.extensionPoint of ExtensionPointHover -> runMaybeT $ do - let hoveredTy = findTypeAtPos ast pos - hoveredQid = fst <$> findQualIdentAtPos ast pos - hoveredMid = fst <$> findModuleIdentAtPos ast pos + let symbol = listToMaybe . fst =<< resolveAtPos store ast pos timeoutSecs = 10 timeoutMicros = timeoutSecs * 1_000_000 templateParams = [ ("currentFile", T.pack (fromMaybe "" (uriToFilePath uri))) @@ -88,10 +84,9 @@ extensionHover ast@(moduleIdentifier -> mid) pos@(J.Position l c) uri e = case e , ("currentModule", ppToText mid) , ("line", T.pack (show l)) , ("column", T.pack (show c)) - , ("expression", maybe "" (.exprText) hoveredTy) - , ("type", maybe "" (ppPredTypeToText mid) ((.typeAnnotation) =<< hoveredTy)) - , ("identifier", maybe "" (ppToText . CI.qidIdent) hoveredQid) - , ("module", maybe "" ppToText (hoveredMid <|> (CI.qidModule =<< hoveredQid))) + , ("type", fromMaybe "" ((.printedType) =<< symbol)) + , ("identifier", maybe "" (.ident) symbol) + , ("module", maybe "" symbolParentIdent symbol) ] :: [(T.Text, T.Text)] applyParam p = T.replace ("{" <> p <> "}") evalTemplate t = foldr (uncurry applyParam) t templateParams