5f3b05a8f2
* Implement completion support Completes the following: - environment variables - local imports - identifiers in scope (as well as built-ins) - record projections - union constructors * Add support for general dependent types Removes the non-dependent let path. Needed since #1164 added support for general dependent types. * Remove unused import * Use monad instance to cast between `Expr Src _` As suggested by @Gabriel439: Use `typeOf (do _ <- expr; holeExpr)` instead of `fmap undefined expr`. In the absence of `Embed` constructors (in this case `Import`s) the two are equivalent. * Simplify completeFromContext Caught by @Gabriel439 * Remove debug code * Add 1s timeout to listDirectory call As pointed out by @Gabriel439, listDirectory can be a potentially expensive operation. Adding a timeout should improve the user experience. * Fix unclean merge
103 lines
5.1 KiB
Haskell
103 lines
5.1 KiB
Haskell
{-| This is the entry point for the LSP server. -}
|
|
module Dhall.LSP.Server(run) where
|
|
|
|
import Control.Concurrent.MVar
|
|
import Data.Default
|
|
import qualified Language.Haskell.LSP.Control as LSP.Control
|
|
import qualified Language.Haskell.LSP.Core as LSP.Core
|
|
|
|
import qualified Language.Haskell.LSP.Types as J
|
|
|
|
import Data.Text (Text)
|
|
import qualified System.Log.Logger
|
|
|
|
import Dhall.LSP.State
|
|
import Dhall.LSP.Handlers (nullHandler, wrapHandler, hoverHandler,
|
|
didOpenTextDocumentNotificationHandler, didSaveTextDocumentNotificationHandler,
|
|
executeCommandHandler, documentFormattingHandler, documentLinkHandler,
|
|
completionHandler)
|
|
|
|
-- | The main entry point for the LSP server.
|
|
run :: Maybe FilePath -> IO ()
|
|
run mlog = do
|
|
setupLogger mlog
|
|
state <- newEmptyMVar
|
|
|
|
-- these two are stubs since we do not use a config
|
|
let onInitialConfiguration :: J.InitializeRequest -> Either Text ()
|
|
onInitialConfiguration _ = Right ()
|
|
let onConfigurationChange :: J.DidChangeConfigurationNotification -> Either Text ()
|
|
onConfigurationChange _ = Right ()
|
|
|
|
-- Callback that is called when the LSP server is started; makes the lsp
|
|
-- state (LspFuncs) available to the message handlers through the `state` MVar.
|
|
let onStartup :: LSP.Core.LspFuncs () -> IO (Maybe J.ResponseError)
|
|
onStartup lsp = do
|
|
putMVar state (initialState lsp)
|
|
return Nothing
|
|
|
|
_ <- LSP.Control.run (LSP.Core.InitializeCallbacks {..})
|
|
(lspHandlers state)
|
|
lspOptions
|
|
Nothing
|
|
return ()
|
|
|
|
-- | sets the output logger.
|
|
-- | if no filename is provided then logger is disabled, if input is string `[OUTPUT]` then log goes to stderr,
|
|
-- | which then redirects inside VSCode to the output pane of the plugin.
|
|
setupLogger :: Maybe FilePath -> IO () -- TODO: ADD verbosity
|
|
setupLogger Nothing = pure ()
|
|
setupLogger (Just "[OUTPUT]") = LSP.Core.setupLogger Nothing [] System.Log.Logger.DEBUG
|
|
setupLogger file = LSP.Core.setupLogger file [] System.Log.Logger.DEBUG
|
|
|
|
|
|
-- Tells the LSP client to notify us about file changes. Handled behind the
|
|
-- scenes by haskell-lsp (in Language.Haskell.LSP.VFS); we don't handle the
|
|
-- corresponding notifications ourselves.
|
|
syncOptions :: J.TextDocumentSyncOptions
|
|
syncOptions = J.TextDocumentSyncOptions
|
|
{ J._openClose = Just True
|
|
, J._change = Just J.TdSyncIncremental
|
|
, J._willSave = Just False
|
|
, J._willSaveWaitUntil = Just False
|
|
, J._save = Just $ J.SaveOptions $ Just False
|
|
}
|
|
|
|
-- Server capabilities. Tells the LSP client that we can execute commands etc.
|
|
lspOptions :: LSP.Core.Options
|
|
lspOptions = def { LSP.Core.textDocumentSync = Just syncOptions
|
|
, LSP.Core.completionProvider =
|
|
Just (J.CompletionOptions {
|
|
_resolveProvider = Nothing
|
|
, _triggerCharacters = Just [":", ".", "/"] })
|
|
, LSP.Core.executeCommandProvider =
|
|
-- Note that this registers the dhall.server.lint command
|
|
-- with VSCode, which means that our plugin can't expose a
|
|
-- command of the same name. In the case of dhall.lint we
|
|
-- name the server-side command dhall.server.lint to work
|
|
-- around this peculiarity.
|
|
Just (J.ExecuteCommandOptions
|
|
(J.List ["dhall.server.lint",
|
|
"dhall.server.annotateLet",
|
|
"dhall.server.freezeImport",
|
|
"dhall.server.freezeAllImports"]))
|
|
, LSP.Core.documentLinkProvider =
|
|
Just (J.DocumentLinkOptions { _resolveProvider = Just False })
|
|
}
|
|
|
|
lspHandlers :: MVar ServerState -> LSP.Core.Handlers
|
|
lspHandlers state
|
|
= def { LSP.Core.initializedHandler = Just $ wrapHandler state nullHandler
|
|
, LSP.Core.hoverHandler = Just $ wrapHandler state hoverHandler
|
|
, LSP.Core.didOpenTextDocumentNotificationHandler = Just $ wrapHandler state didOpenTextDocumentNotificationHandler
|
|
, LSP.Core.didChangeTextDocumentNotificationHandler = Just $ wrapHandler state nullHandler
|
|
, LSP.Core.didSaveTextDocumentNotificationHandler = Just $ wrapHandler state didSaveTextDocumentNotificationHandler
|
|
, LSP.Core.didCloseTextDocumentNotificationHandler = Just $ wrapHandler state nullHandler
|
|
, LSP.Core.cancelNotificationHandler = Just $ wrapHandler state nullHandler
|
|
, LSP.Core.responseHandler = Just $ wrapHandler state nullHandler
|
|
, LSP.Core.executeCommandHandler = Just $ wrapHandler state executeCommandHandler
|
|
, LSP.Core.documentFormattingHandler = Just $ wrapHandler state documentFormattingHandler
|
|
, LSP.Core.documentLinkHandler = Just $ wrapHandler state documentLinkHandler
|
|
, LSP.Core.completionHandler = Just $ wrapHandler state completionHandler
|
|
}
|