Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
52 changes: 44 additions & 8 deletions haskell-lsp-types/src/Language/Haskell/LSP/Types/DataTypesJSON.hs
Original file line number Diff line number Diff line change
Expand Up @@ -211,13 +211,32 @@ interface CompletionOptions {
* The characters that trigger completion automatically.
*/
triggerCharacters?: string[];

/**
* The list of all possible characters that commit a completion. This field can be used
* if clients don't support individual commmit characters per completion item. See
* `ClientCapabilities.textDocument.completion.completionItem.commitCharactersSupport`.
*
* If a server provides both `allCommitCharacters` and commit characters on an individual
* completion item the once on the completion item win.
*
* @since 3.2.0
*/
allCommitCharacters?: string[];
}
-}

data CompletionOptions =
CompletionOptions
{ _resolveProvider :: Maybe Bool
, _triggerCharacters :: Maybe [String]
{ _resolveProvider :: Maybe Bool
-- | The characters that trigger completion automatically.
, _triggerCharacters :: Maybe [String]
-- | The list of all possible characters that commit a completion. This field can be used
-- if clients don't support individual commmit characters per completion item. See
-- `_commitCharactersSupport`.
-- Since LSP 3.2.0
-- @since 0.18.0.0
, _allCommitCharacters :: Maybe [String]
} deriving (Read,Show,Eq)

deriveJSON lspOptions {omitNothingFields = True } ''CompletionOptions
Expand All @@ -232,11 +251,28 @@ interface SignatureHelpOptions {
* The characters that trigger signature help automatically.
*/
triggerCharacters?: string[];
/**
* List of characters that re-trigger signature help.
*
* These trigger characters are only active when signature help is already showing. All trigger characters
* are also counted as re-trigger characters.
*
* @since 3.15.0
*/
-}

data SignatureHelpOptions =
SignatureHelpOptions
{ _triggerCharacters :: Maybe [String]
{ -- | The characters that trigger signature help automatically.
_triggerCharacters :: Maybe [String]

-- | List of characters that re-trigger signature help.
-- These trigger characters are only active when signature help is already showing. All trigger characters
-- are also counted as re-trigger characters.
--
-- Since LSP 3.15.0
-- @since 0.18.0.0
, _retriggerCharacters :: Maybe [String]
} deriving (Read,Show,Eq)

deriveJSON lspOptions ''SignatureHelpOptions
Expand Down Expand Up @@ -304,7 +340,7 @@ interface DocumentOnTypeFormattingOptions {
data DocumentOnTypeFormattingOptions =
DocumentOnTypeFormattingOptions
{ _firstTriggerCharacter :: Text
, _moreTriggerCharacter :: Maybe [String]
, _moreTriggerCharacter :: Maybe [Text]
} deriving (Read,Show,Eq)

deriveJSON lspOptions ''DocumentOnTypeFormattingOptions
Expand All @@ -327,7 +363,7 @@ export interface DocumentLinkOptions {

data DocumentLinkOptions =
DocumentLinkOptions
{ -- |Document links have a resolve provider as well.
{ -- | Document links have a resolve provider as well.
_resolveProvider :: Maybe Bool
} deriving (Show, Read, Eq)

Expand All @@ -352,7 +388,7 @@ export interface RenameOptions {
data RenameOptions =
RenameOptionsStatic Bool
| RenameOptions
{ -- |Renames should be checked and tested before being executed.
{ -- | Renames should be checked and tested before being executed.
_prepareProvider :: Maybe Bool
} deriving (Show, Read, Eq)

Expand Down Expand Up @@ -451,7 +487,7 @@ data TextDocumentSyncOptions =
-- | Will save wait until requests are sent to the server.
, _willSaveWaitUntil :: Maybe Bool

-- |Save notifications are sent to the server.
-- | Save notifications are sent to the server.
, _save :: Maybe SaveOptions
} deriving (Show, Read, Eq)

Expand Down Expand Up @@ -673,7 +709,7 @@ deriveJSON lspOptions ''WorkspaceFolderOptions

data WorkspaceOptions =
WorkspaceOptions
{ -- |The server supports workspace folder. Since LSP 3.6
{ -- | The server supports workspace folder. Since LSP 3.6
--
-- @since 0.7.0.0
_workspaceFolders :: Maybe WorkspaceFolderOptions
Expand Down
201 changes: 124 additions & 77 deletions src/Language/Haskell/LSP/Core.hs
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,14 @@ import Control.Concurrent.Async
import qualified Control.Exception as E
import Control.Monad
import Control.Monad.IO.Class
import Control.Lens ( (<&>), (^.) )
import Control.Lens ( (<&>), (^.), (^?), _Just )
import qualified Data.Aeson as J
import qualified Data.ByteString.Lazy as BSL
import qualified Data.ByteString.Lazy.Char8 as B
import Data.Default
import qualified Data.HashMap.Strict as HM
import qualified Data.List as L
import Data.List.NonEmpty (NonEmpty(..))
import qualified Data.Map as Map
import Data.Maybe
import Data.Monoid
Expand Down Expand Up @@ -95,29 +96,37 @@ data ProgressData = ProgressData { progressNextId :: !Int

-- ---------------------------------------------------------------------

-- | Language Server Protocol options supported by the given language server.
-- These are automatically turned into capabilities reported to the client
-- during initialization.
-- | Language Server Protocol options that the server may configure.
-- If you set handlers for some requests, you may need to set some of these options.
data Options =
Options
{ textDocumentSync :: Maybe J.TextDocumentSyncOptions
, completionProvider :: Maybe J.CompletionOptions
, signatureHelpProvider :: Maybe J.SignatureHelpOptions
, typeDefinitionProvider :: Maybe J.GotoOptions
, implementationProvider :: Maybe J.GotoOptions
, codeActionProvider :: Maybe J.CodeActionOptions
, codeLensProvider :: Maybe J.CodeLensOptions
, documentOnTypeFormattingProvider :: Maybe J.DocumentOnTypeFormattingOptions
, renameProvider :: Maybe J.RenameOptions
, documentLinkProvider :: Maybe J.DocumentLinkOptions
, colorProvider :: Maybe J.ColorOptions
, foldingRangeProvider :: Maybe J.FoldingRangeOptions
, executeCommandProvider :: Maybe J.ExecuteCommandOptions
-- | The characters that trigger completion automatically.
, completionTriggerCharacters :: Maybe [Char]
-- | The list of all possible characters that commit a completion. This field can be used
-- if clients don't support individual commmit characters per completion item. See
-- `_commitCharactersSupport`.
, completionAllCommitCharacters :: Maybe [Char]
-- | The characters that trigger signature help automatically.
, signatureHelpTriggerCharacters :: Maybe [Char]
-- | List of characters that re-trigger signature help.
-- These trigger characters are only active when signature help is already showing. All trigger characters
-- are also counted as re-trigger characters.
, signatureHelpRetriggerCharacters :: Maybe [Char]
-- | CodeActionKinds that this server may return.
-- The list of kinds may be generic, such as `CodeActionKind.Refactor`, or the server
-- may list out every specific kind they provide.
, codeActionKinds :: Maybe [J.CodeActionKind]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the API for this isn’t quite right (but that’s not new to this PR):
The LSP spec states

The CodeActionOptions return type is only valid if the client signals code action literal support via the property textDocument.codeAction.codeActionLiteralSupport.

However, the current API requires you to specify the options before you can inspect the capabilities so you can’t actually specify this properly.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. I think we can just return CodeActionOptionsStatic True in the case when the client capabilities do not declare support for code action literals, and pass on the code action options in the case that they do

-- | The list of characters that triggers on type formatting.
-- If you set `documentOnTypeFormattingHandler`, you **must** set this.
, documentOnTypeFormattingTriggerCharacters :: Maybe (NonEmpty Char)
-- | The commands to be executed on the server.
-- If you set `executeCommandHandler`, you **must** set this.
, executeCommandCommands :: Maybe [Text]
}

instance Default Options where
def = Options Nothing Nothing Nothing Nothing Nothing
Nothing Nothing Nothing Nothing Nothing
Nothing Nothing Nothing

-- | A function to publish diagnostics. It aggregates all diagnostics pertaining
Expand Down Expand Up @@ -236,7 +245,7 @@ data Handlers =
, colorPresentationHandler :: !(Maybe (Handler J.ColorPresentationRequest))
, documentFormattingHandler :: !(Maybe (Handler J.DocumentFormattingRequest))
, documentRangeFormattingHandler :: !(Maybe (Handler J.DocumentRangeFormattingRequest))
, documentTypeFormattingHandler :: !(Maybe (Handler J.DocumentOnTypeFormattingRequest))
, documentOnTypeFormattingHandler :: !(Maybe (Handler J.DocumentOnTypeFormattingRequest))
, renameHandler :: !(Maybe (Handler J.RenameRequest))
, prepareRenameHandler :: !(Maybe (Handler J.PrepareRenameRequest))
, foldingRangeHandler :: !(Maybe (Handler J.FoldingRangeRequest))
Expand Down Expand Up @@ -359,7 +368,7 @@ handlerMap _ h J.TextDocumentDocumentHighlight = hh nop ReqDocumentHighlights
handlerMap _ h J.TextDocumentDocumentSymbol = hh nop ReqDocumentSymbols $ documentSymbolHandler h
handlerMap _ h J.TextDocumentFormatting = hh nop ReqDocumentFormatting $ documentFormattingHandler h
handlerMap _ h J.TextDocumentRangeFormatting = hh nop ReqDocumentRangeFormatting $ documentRangeFormattingHandler h
handlerMap _ h J.TextDocumentOnTypeFormatting = hh nop ReqDocumentOnTypeFormatting $ documentTypeFormattingHandler h
handlerMap _ h J.TextDocumentOnTypeFormatting = hh nop ReqDocumentOnTypeFormatting $ documentOnTypeFormattingHandler h
handlerMap _ h J.TextDocumentCodeAction = hh nop ReqCodeAction $ codeActionHandler h
handlerMap _ h J.TextDocumentCodeLens = hh nop ReqCodeLens $ codeLensHandler h
handlerMap _ h J.CodeLensResolve = hh nop ReqCodeLensResolve $ codeLensResolveHandler h
Expand Down Expand Up @@ -831,67 +840,105 @@ initializeRequestHandler' onStartup mHandler tvarCtx req@(J.RequestMessage _ ori
sendResponse tvarCtx $ RspError $ makeResponseError (J.responseId origId) errResp

Nothing -> do
let capa = serverCapabilities (getCapabilities params) (resOptions ctx) (resHandlers ctx)
-- TODO: wrap this up into a fn to create a response message
res = J.ResponseMessage "2.0" (J.responseId origId) (Just $ J.InitializeResponseCapabilities capa) Nothing

let
h = resHandlers ctx
o = resOptions ctx

supported (Just _) = Just True
supported Nothing = Nothing

-- If a dynamic setting is provided use it, else set a
-- static True if there is a handler.
static (Just d) _ = Just d
static _ (Just _) = Just (J.GotoOptionsStatic True)
static _ Nothing = Nothing

static' (Just d) (Just _) = Just d
static' _ (Just _) = Just (J.CodeActionOptionsStatic True)
static' _ _ = Nothing

sync = case textDocumentSync o of
Just x -> Just (J.TDSOptions x)
Nothing -> Nothing

workspace = J.WorkspaceOptions workspaceFolder
workspaceFolder = case didChangeWorkspaceFoldersNotificationHandler h of
Just _ -> Just $
-- sign up to receive notifications
J.WorkspaceFolderOptions (Just True) (Just (J.WorkspaceFolderChangeNotificationsBool True))
Nothing -> Nothing
sendResponse tvarCtx $ RspInitialize res

capa =
J.InitializeResponseCapabilitiesInner
{ J._textDocumentSync = sync
, J._hoverProvider = supported (hoverHandler h)
, J._completionProvider = completionProvider o
, J._signatureHelpProvider = signatureHelpProvider o
, J._definitionProvider = supported (definitionHandler h)
, J._typeDefinitionProvider = static (typeDefinitionProvider o) (typeDefinitionHandler h)
, J._implementationProvider = implementationProvider o
, J._referencesProvider = supported (referencesHandler h)
, J._documentHighlightProvider = supported (documentHighlightHandler h)
, J._documentSymbolProvider = supported (documentSymbolHandler h)
, J._workspaceSymbolProvider = supported (workspaceSymbolHandler h)
, J._codeActionProvider = static' (codeActionProvider o) (codeActionHandler h)
, J._codeLensProvider = codeLensProvider o
, J._documentFormattingProvider = supported (documentFormattingHandler h)
, J._documentRangeFormattingProvider = supported (documentRangeFormattingHandler h)
, J._documentOnTypeFormattingProvider = documentOnTypeFormattingProvider o
, J._renameProvider = renameProvider o
, J._documentLinkProvider = documentLinkProvider o
, J._colorProvider = colorProvider o
, J._foldingRangeProvider = foldingRangeProvider o
, J._executeCommandProvider = executeCommandProvider o
, J._workspace = Just workspace
-- TODO: Add something for experimental
, J._experimental = Nothing :: Maybe J.Value
}

-- TODO: wrap this up into a fn to create a response message
res = J.ResponseMessage "2.0" (J.responseId origId) (Just $ J.InitializeResponseCapabilities capa) Nothing
-- | Infers the capabilities based on registered handlers, and sets the appropriate options.
-- A provider should be set to Nothing if the server does not support it, unless it is a
-- static option.
serverCapabilities :: C.ClientCapabilities -> Options -> Handlers -> J.InitializeResponseCapabilitiesInner
serverCapabilities clientCaps o h =
J.InitializeResponseCapabilitiesInner
{ J._textDocumentSync = sync
, J._hoverProvider = supported (hoverHandler h)
, J._completionProvider = completionProvider
, J._signatureHelpProvider = signatureHelpProvider
, J._definitionProvider = supported (definitionHandler h)
, J._typeDefinitionProvider = Just $ J.GotoOptionsStatic $ isJust $ typeDefinitionHandler h
, J._implementationProvider = Just $ J.GotoOptionsStatic $ isJust $ typeDefinitionHandler h
, J._referencesProvider = supported (referencesHandler h)
, J._documentHighlightProvider = supported (documentHighlightHandler h)
, J._documentSymbolProvider = supported (documentSymbolHandler h)
, J._workspaceSymbolProvider = supported (workspaceSymbolHandler h)
, J._codeActionProvider = codeActionProvider
, J._codeLensProvider = supported' (codeLensHandler h) $ J.CodeLensOptions $
supported (codeLensResolveHandler h)
, J._documentFormattingProvider = supported (documentFormattingHandler h)
, J._documentRangeFormattingProvider = supported (documentRangeFormattingHandler h)
, J._documentOnTypeFormattingProvider = documentOnTypeFormattingProvider
, J._renameProvider = Just $ J.RenameOptionsStatic $ isJust $ renameHandler h
, J._documentLinkProvider = supported' (documentLinkHandler h) $ J.DocumentLinkOptions $
Just $ isJust $ documentLinkResolveHandler h
, J._colorProvider = Just $ J.ColorOptionsStatic $ isJust $ documentColorHandler h
, J._foldingRangeProvider = Just $ J.FoldingRangeOptionsStatic $ isJust $ foldingRangeHandler h
, J._executeCommandProvider = executeCommandProvider
, J._workspace = Just workspace
-- TODO: Add something for experimental
, J._experimental = Nothing :: Maybe J.Value
}
where
supported x = supported' x True

supported' (Just _) = Just
supported' Nothing = const Nothing

singleton :: a -> [a]
singleton x = [x]

completionProvider
| isJust $ completionHandler h = Just $
J.CompletionOptions
(Just $ isJust $ completionResolveHandler h)
(map singleton <$> completionTriggerCharacters o)
(map singleton <$> completionAllCommitCharacters o)
| otherwise = Nothing

clientSupportsCodeActionKinds = isJust $
clientCaps ^? J.textDocument . _Just . J.codeAction . _Just . J.codeActionLiteralSupport

codeActionProvider
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This breaks ghcide in a way that I don’t know how to fix. We currently set this to CodeActionOptionsStatic True and as mentioned above, I can’t set the individual flags properly since I learn the capabilities after I need to specify the options. Maybe for now, we could use something like

isJust $ codeActionHandler h = maybe (J.CodeActionOptionsStatic True) (CodeActionOptions . Just) (codeActionKinds o)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sounds reasonable. I left it this way as it is possible to return a CodeActionOptions with a null CodeActionKinds inside it (which doesn't make much sense given the previous comment), so I'm happy enough to default to the static capability if the kinds in the LSP options are set to Nothing

| clientSupportsCodeActionKinds
, isJust $ codeActionHandler h = Just $ maybe (J.CodeActionOptionsStatic True) (J.CodeActionOptions . Just) (codeActionKinds o)
| isJust $ codeActionHandler h = Just (J.CodeActionOptionsStatic True)
| otherwise = Just (J.CodeActionOptionsStatic False)

signatureHelpProvider
| isJust $ signatureHelpHandler h = Just $
J.SignatureHelpOptions
(map singleton <$> signatureHelpTriggerCharacters o)
(map singleton <$> signatureHelpRetriggerCharacters o)
| otherwise = Nothing

documentOnTypeFormattingProvider
| isJust $ documentOnTypeFormattingHandler h
, Just (first :| rest) <- documentOnTypeFormattingTriggerCharacters o = Just $
J.DocumentOnTypeFormattingOptions (T.pack [first]) (Just (map (T.pack . singleton) rest))
| isJust $ documentOnTypeFormattingHandler h
, Nothing <- documentOnTypeFormattingTriggerCharacters o =
error "documentOnTypeFormattingTriggerCharacters needs to be set if a documentOnTypeFormattingHandler is set"
| otherwise = Nothing

executeCommandProvider
| isJust $ executeCommandHandler h
, Just cmds <- executeCommandCommands o = Just (J.ExecuteCommandOptions (J.List cmds))
| isJust $ executeCommandHandler h
, Nothing <- executeCommandCommands o =
error "executeCommandCommands needs to be set if a executeCommandHandler is set"
| otherwise = Nothing

sync = case textDocumentSync o of
Just x -> Just (J.TDSOptions x)
Nothing -> Nothing

sendResponse tvarCtx $ RspInitialize res
workspace = J.WorkspaceOptions workspaceFolder
workspaceFolder = case didChangeWorkspaceFoldersNotificationHandler h of
Just _ -> Just $
-- sign up to receive notifications
J.WorkspaceFolderOptions (Just True) (Just (J.WorkspaceFolderChangeNotificationsBool True))
Nothing -> Nothing

progressCancelHandler :: TVar (LanguageContextData config) -> J.WorkDoneProgressCancelNotification -> IO ()
progressCancelHandler tvarCtx (J.NotificationMessage _ _ (J.WorkDoneProgressCancelParams tid)) = do
Expand Down